');
- $.each(folders, function(k,v)
- {
- select.append($('' + k + ' '));
- });
-
- $('#redactor_image_box').before(select);
- select.change(onchangeFunc);
- }
-
- }, this));
- }
- else
- {
- $('#redactor_tabs a').eq(1).remove();
- }
-
- if (this.opts.imageUpload !== false)
- {
-
- // dragupload
- if (this.opts.uploadCrossDomain === false && this.isMobile() === false)
- {
-
- if ($('#redactor_file').size() !== 0)
- {
- $('#redactor_file').dragupload(
- {
- url: this.opts.imageUpload,
- uploadFields: this.opts.uploadFields,
- success: $.proxy(this.imageUploadCallback, this),
- error: $.proxy(this.opts.imageUploadErrorCallback, this)
- });
- }
- }
-
- // ajax upload
- this.uploadInit('redactor_file',
- {
- auto: true,
- url: this.opts.imageUpload,
- success: $.proxy(this.imageUploadCallback, this),
- error: $.proxy(this.opts.imageUploadErrorCallback, this)
- });
- }
- else
- {
- $('.redactor_tab').hide();
- if (this.opts.imageGetJson === false)
- {
- $('#redactor_tabs').remove();
- $('#redactor_tab3').show();
- }
- else
- {
- var tabs = $('#redactor_tabs a');
- tabs.eq(0).remove();
- tabs.eq(1).addClass('redactor_tabs_act');
- $('#redactor_tab2').show();
- }
- }
-
- $('#redactor_upload_btn').click($.proxy(this.imageUploadCallbackLink, this));
-
- if (this.opts.imageUpload === false && this.opts.imageGetJson === false)
- {
- setTimeout(function()
- {
- $('#redactor_file_link').focus();
- }, 200);
-
- }
-
- }, this);
-
- this.modalInit(RLANG.image, this.opts.modal_image, 610, callback);
-
- },
- imageSetThumb: function(e)
- {
- this._imageSet(' ', true);
- },
- imageUploadCallbackLink: function()
- {
- if ($('#redactor_file_link').val() !== '')
- {
- var data = ' ';
- this._imageSet(data, true);
- }
- else
- {
- this.modalClose();
- }
- },
- imageUploadCallback: function(data)
- {
- this._imageSet(data);
- },
- _imageSet: function(json, link)
- {
- this.restoreSelection();
-
- if (json !== false)
- {
- var html = '';
- if (link !== true)
- {
- html = '
';
- }
- else
- {
- html = json;
- }
-
- this.execCommand('inserthtml', html);
-
- // upload image callback
- if (link !== true && typeof this.opts.imageUploadCallback === 'function')
- {
- this.opts.imageUploadCallback(this, json);
- }
- }
-
- this.modalClose();
- this.observeImages();
- },
-
- // INSERT LINK
- showLink: function()
- {
- this.saveSelection();
-
- var callback = $.proxy(function()
- {
- this.insert_link_node = false;
- var sel = this.getSelection();
- var url = '', text = '', target = '';
-
- if (this.browser('msie'))
- {
- var parent = this.getParentNode();
- if (parent.nodeName === 'A')
- {
- this.insert_link_node = $(parent);
- text = this.insert_link_node.text();
- url = this.insert_link_node.attr('href');
- target = this.insert_link_node.attr('target');
- }
- else
- {
- if (this.oldIE())
- {
- text = sel.text;
- }
- else
- {
- text = sel.toString();
- }
- }
- }
- else
- {
- if (sel && sel.anchorNode && sel.anchorNode.parentNode.tagName === 'A')
- {
- url = sel.anchorNode.parentNode.href;
- text = sel.anchorNode.parentNode.text;
- target = sel.anchorNode.parentNode.target;
-
- if (sel.toString() === '')
- {
- this.insert_link_node = sel.anchorNode.parentNode;
- }
- }
- else
- {
- text = sel.toString();
- }
- }
-
- $('.redactor_link_text').val(text);
-
- var thref = self.location.href.replace(/\/$/i, '');
- var turl = url.replace(thref, '');
-
- if (url.search('mailto:') === 0)
- {
- this.setModalTab(2);
-
- $('#redactor_tab_selected').val(2);
- $('#redactor_link_mailto').val(url.replace('mailto:', ''));
- }
- else if (turl.search(/^#/gi) === 0)
- {
- this.setModalTab(3);
-
- $('#redactor_tab_selected').val(3);
- $('#redactor_link_anchor').val(turl.replace(/^#/gi, ''));
- }
- else
- {
- $('#redactor_link_url').val(turl);
- }
-
- if (target === '_blank')
- {
- $('#redactor_link_blank').attr('checked', true);
- }
-
- $('#redactor_insert_link_btn').click($.proxy(this.insertLink, this));
-
- setTimeout(function()
- {
- $('#redactor_link_url').focus();
- }, 200);
-
- }, this);
-
- this.modalInit(RLANG.link, this.opts.modal_link, 460, callback);
-
- },
- insertLink: function()
- {
- var tab_selected = $('#redactor_tab_selected').val();
- var link = '', text = '', target = '';
-
- if (tab_selected === '1') // url
- {
- link = $('#redactor_link_url').val();
- text = $('#redactor_link_url_text').val();
-
- if ($('#redactor_link_blank').attr('checked'))
- {
- target = ' target="_blank"';
- }
-
- // test url
- var pattern = '/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/';
- //var pattern = '((xn--)?[a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}';
- var re = new RegExp('^(http|ftp|https)://' + pattern,'i');
- var re2 = new RegExp('^' + pattern,'i');
- if (link.search(re) == -1 && link.search(re2) == 0 && this.opts.protocol !== false)
- {
- link = this.opts.protocol + link;
- }
-
- }
- else if (tab_selected === '2') // mailto
- {
- link = 'mailto:' + $('#redactor_link_mailto').val();
- text = $('#redactor_link_mailto_text').val();
- }
- else if (tab_selected === '3') // anchor
- {
- link = '#' + $('#redactor_link_anchor').val();
- text = $('#redactor_link_anchor_text').val();
- }
-
- this._insertLink('' + text + ' ', $.trim(text), link, target);
-
- },
- _insertLink: function(a, text, link, target)
- {
- this.$editor.focus();
- this.restoreSelection();
-
- if (text !== '')
- {
- if (this.insert_link_node)
- {
- $(this.insert_link_node).text(text);
- $(this.insert_link_node).attr('href', link);
- if (target !== '')
- {
- $(this.insert_link_node).attr('target', target);
- }
- else
- {
- $(this.insert_link_node).removeAttr('target');
- }
-
- this.syncCode();
- }
- else
- {
- this.execCommand('inserthtml', a);
- }
- }
-
- this.modalClose();
- },
-
- // INSERT FILE
- showFile: function()
- {
- this.saveSelection();
-
- var callback = $.proxy(function()
- {
- var sel = this.getSelection();
-
- var text = '';
-
- if (this.oldIE())
- {
- text = sel.text;
- }
- else
- {
- text = sel.toString();
- }
-
- $('#redactor_filename').val(text);
-
- // dragupload
- if (this.opts.uploadCrossDomain === false && this.isMobile() === false)
- {
- $('#redactor_file').dragupload(
- {
- url: this.opts.fileUpload,
- uploadFields: this.opts.uploadFields,
- success: $.proxy(this.fileUploadCallback, this),
- error: $.proxy(this.opts.fileUploadErrorCallback, this)
- });
- }
-
- this.uploadInit('redactor_file',
- {
- auto: true,
- url: this.opts.fileUpload,
- success: $.proxy(this.fileUploadCallback, this),
- error: $.proxy(this.opts.fileUploadErrorCallback, this)
- });
-
- }, this);
-
- this.modalInit(RLANG.file, this.opts.modal_file, 500, callback);
- },
- fileUploadCallback: function(json)
- {
- this.restoreSelection();
-
- if (json !== false)
- {
- var text = $('#redactor_filename').val();
-
- if (text === '')
- {
- text = json.filename;
- }
-
- var link = '' + text + ' ';
-
- // chrome fix
- if (this.browser('webkit') && !!this.window.chrome)
- {
- link = link + ' ';
- }
-
- this.execCommand('inserthtml', link);
-
- // file upload callback
- if (typeof this.opts.fileUploadCallback === 'function')
- {
- this.opts.fileUploadCallback(this, json);
- }
- }
-
- this.modalClose();
- },
-
-
-
- // MODAL
- modalInit: function(title, content, width, callback)
- {
- // modal overlay
- if ($('#redactor_modal_overlay').size() === 0)
- {
- this.overlay = $('
');
- $('body').prepend(this.overlay);
- }
-
- if (this.opts.overlay)
- {
- $('#redactor_modal_overlay').show();
- $('#redactor_modal_overlay').click($.proxy(this.modalClose, this));
- }
-
- if ($('#redactor_modal').size() === 0)
- {
- this.modal = $('');
- $('body').append(this.modal);
- }
-
- $('#redactor_modal_close').click($.proxy(this.modalClose, this));
-
- this.hdlModalClose = $.proxy(function(e) { if ( e.keyCode === 27) { this.modalClose(); return false; } }, this);
-
- $(document).keyup(this.hdlModalClose);
- this.$editor.keyup(this.hdlModalClose);
-
- // set content
- if (content.indexOf('#') == 0)
- {
- $('#redactor_modal_inner').empty().append($(content).html());
- }
- else
- {
- $('#redactor_modal_inner').empty().append(content);
- }
-
-
- $('#redactor_modal_header').html(title);
-
- // draggable
- if (typeof $.fn.draggable !== 'undefined')
- {
- $('#redactor_modal').draggable({ handle: '#redactor_modal_header' });
- $('#redactor_modal_header').css('cursor', 'move');
- }
-
- // tabs
- if ($('#redactor_tabs').size() !== 0)
- {
- var that = this;
- $('#redactor_tabs a').each(function(i,s)
- {
- i++;
- $(s).click(function()
- {
- $('#redactor_tabs a').removeClass('redactor_tabs_act');
- $(this).addClass('redactor_tabs_act');
- $('.redactor_tab').hide();
- $('#redactor_tab' + i).show();
- $('#redactor_tab_selected').val(i);
-
- if (that.isMobile() === false)
- {
- var height = $('#redactor_modal').outerHeight();
- $('#redactor_modal').css('margin-top', '-' + (height+10)/2 + 'px');
- }
- });
- });
- }
-
- $('#redactor_modal .redactor_btn_modal_close').click($.proxy(this.modalClose, this));
-
- if (this.isMobile() === false)
- {
- $('#redactor_modal').css({ position: 'fixed', top: '-2000px', left: '50%', width: width + 'px', marginLeft: '-' + (width+60)/2 + 'px' }).show();
-
- this.modalSaveBodyOveflow = $(document.body).css('overflow');
- $(document.body).css('overflow', 'hidden');
- }
- else
- {
- $('#redactor_modal').css({ position: 'fixed', width: '100%', height: '100%', top: '0', left: '0', margin: '0', minHeight: '300px' }).show();
- }
-
- // callback
- if (typeof callback === 'function')
- {
- callback();
- }
-
- if (this.isMobile() === false)
- {
- setTimeout(function()
- {
- var height = $('#redactor_modal').outerHeight();
- $('#redactor_modal').css({ top: '50%', height: 'auto', minHeight: 'auto', marginTop: '-' + (height+10)/2 + 'px' });
-
- }, 20);
- }
-
- },
- modalClose: function()
- {
- $('#redactor_modal_close').unbind('click', this.modalClose);
- $('#redactor_modal').fadeOut('fast', $.proxy(function()
- {
- $('#redactor_modal_inner').html('');
-
- if (this.opts.overlay)
- {
- $('#redactor_modal_overlay').hide();
- $('#redactor_modal_overlay').unbind('click', this.modalClose);
- }
-
- $(document).unbind('keyup', this.hdlModalClose);
- this.$editor.unbind('keyup', this.hdlModalClose);
-
- }, this));
-
-
- if (this.isMobile() === false)
- {
- $(document.body).css('overflow', this.modalSaveBodyOveflow ? this.modalSaveBodyOveflow : 'visible');
- }
-
- return false;
-
- },
- setModalTab: function(num)
- {
- $('.redactor_tab').hide();
- var tabs = $('#redactor_tabs a');
- tabs.removeClass('redactor_tabs_act');
- tabs.eq(num-1).addClass('redactor_tabs_act');
- $('#redactor_tab' + num).show();
- },
-
- // UPLOAD
- uploadInit: function(element, options)
- {
- // Upload Options
- this.uploadOptions = {
- url: false,
- success: false,
- error: false,
- start: false,
- trigger: false,
- auto: false,
- input: false
- };
-
- $.extend(this.uploadOptions, options);
-
- // Test input or form
- if ($('#' + element).size() !== 0 && $('#' + element).get(0).tagName === 'INPUT')
- {
- this.uploadOptions.input = $('#' + element);
- this.element = $($('#' + element).get(0).form);
- }
- else
- {
- this.element = $('#' + element);
- }
-
- this.element_action = this.element.attr('action');
-
- // Auto or trigger
- if (this.uploadOptions.auto)
- {
- $(this.uploadOptions.input).change($.proxy(function()
- {
- this.element.submit(function(e) { return false; });
- this.uploadSubmit();
- }, this));
-
- }
- else if (this.uploadOptions.trigger)
- {
- $('#' + this.uploadOptions.trigger).click($.proxy(this.uploadSubmit, this));
- }
- },
- uploadSubmit : function()
- {
- this.uploadForm(this.element, this.uploadFrame());
- },
- uploadFrame : function()
- {
- this.id = 'f' + Math.floor(Math.random() * 99999);
-
- var d = this.document.createElement('div');
- var iframe = '';
- d.innerHTML = iframe;
- $(d).appendTo("body");
-
- // Start
- if (this.uploadOptions.start)
- {
- this.uploadOptions.start();
- }
-
- $('#' + this.id).load($.proxy(this.uploadLoaded, this));
-
- return this.id;
- },
- uploadForm : function(f, name)
- {
- if (this.uploadOptions.input)
- {
- var formId = 'redactorUploadForm' + this.id;
- var fileId = 'redactorUploadFile' + this.id;
- this.form = $('');
-
- // append hidden fields
- if (this.opts.uploadFields !== false && typeof this.opts.uploadFields === 'object')
- {
- $.each(this.opts.uploadFields, $.proxy(function(k,v)
- {
- if (v.toString().indexOf('#') === 0)
- {
- v = $(v).val();
- }
-
- var hidden = $(' ', {'type': "hidden", 'name': k, 'value': v});
- $(this.form).append(hidden);
-
- }, this));
- }
-
- var oldElement = this.uploadOptions.input;
- var newElement = $(oldElement).clone();
- $(oldElement).attr('id', fileId);
- $(oldElement).before(newElement);
- $(oldElement).appendTo(this.form);
- $(this.form).css('position', 'absolute');
- $(this.form).css('top', '-2000px');
- $(this.form).css('left', '-2000px');
- $(this.form).appendTo('body');
-
- this.form.submit();
- }
- else
- {
- f.attr('target', name);
- f.attr('method', 'POST');
- f.attr('enctype', 'multipart/form-data');
- f.attr('action', this.uploadOptions.url);
-
- this.element.submit();
- }
-
- },
- uploadLoaded : function()
- {
- var i = $('#' + this.id)[0];
- var d;
-
- if (i.contentDocument)
- {
- d = i.contentDocument;
- }
- else if (i.contentWindow)
- {
- d = i.contentWindow.document;
- }
- else
- {
- d = window.frames[this.id].document;
- }
-
- // Success
- if (this.uploadOptions.success)
- {
- if (typeof d !== 'undefined')
- {
- // Remove bizarre tag wrappers around our json data:
- var rawString = d.body.innerHTML;
- var jsonString = rawString.match(/\{(.|\n)*\}/)[0];
- var json = $.parseJSON(jsonString);
-
- if (typeof json.error == 'undefined')
- {
- this.uploadOptions.success(json);
- }
- else
- {
- this.uploadOptions.error(this, json);
- this.modalClose();
- }
- }
- else
- {
- alert('Upload failed!');
- this.modalClose();
- }
- }
-
- this.element.attr('action', this.element_action);
- this.element.attr('target', '');
-
- },
-
- // UTILITY
- browser: function(browser)
- {
- var ua = navigator.userAgent.toLowerCase();
- var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || /(webkit)[ \/]([\w.]+)/.exec(ua) || /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || /(msie) ([\w.]+)/.exec(ua) || ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || [];
-
- if (browser == 'version')
- {
- return match[2];
- }
-
- if (browser == 'webkit')
- {
- return (match[1] == 'chrome' || match[1] == 'webkit');
- }
-
- return match[1] == browser;
- },
- oldIE: function()
- {
- if (this.browser('msie') && parseInt(this.browser('version'), 10) < 9)
- {
- return true;
- }
-
- return false;
- },
- outerHTML: function(s)
- {
- return $("").append($(s).eq(0).clone()).html();
- },
- normalize: function(str)
- {
- return parseInt(str.replace('px',''), 10);
- },
- isMobile: function(ipad)
- {
- if (ipad === true && /(iPhone|iPod|iPad|BlackBerry|Android)/.test(navigator.userAgent))
- {
- return true;
- }
- else if (/(iPhone|iPod|BlackBerry|Android)/.test(navigator.userAgent))
- {
- return true;
- }
- else
- {
- return false;
- }
- }
-
- };
-
-
- // API
- $.fn.getObject = function()
- {
- return this.data('redactor');
- };
-
- $.fn.getEditor = function()
- {
- return this.data('redactor').$editor;
- };
-
- $.fn.getCode = function()
- {
- return $.trim(this.data('redactor').getCode());
- };
-
- $.fn.getText = function()
- {
- return this.data('redactor').$editor.text();
- };
-
- $.fn.getSelected = function()
- {
- return this.data('redactor').getSelectedHtml();
- };
-
- $.fn.setCode = function(html)
- {
- this.data('redactor').setCode(html);
- };
-
- $.fn.insertHtml = function(html)
- {
- this.data('redactor').insertHtml(html);
- };
-
- $.fn.destroyEditor = function()
- {
- this.each(function()
- {
- if (typeof $(this).data('redactor') != 'undefined')
- {
- $(this).data('redactor').destroy();
- $(this).removeData('redactor');
- }
- });
- };
-
- $.fn.setFocus = function()
- {
- this.data('redactor').$editor.focus();
- };
-
- $.fn.execCommand = function(cmd, param)
- {
- this.data('redactor').execCommand(cmd, param);
- };
-
-})(jQuery);
-
-/*
- Plugin Drag and drop Upload v1.0.2
- http://imperavi.com/
- Copyright 2012, Imperavi Inc.
-*/
-(function($){
-
- "use strict";
-
- // Initialization
- $.fn.dragupload = function(options)
- {
- return this.each(function() {
- var obj = new Construct(this, options);
- obj.init();
- });
- };
-
- // Options and variables
- function Construct(el, options) {
-
- this.opts = $.extend({
-
- url: false,
- success: false,
- error: false,
- preview: false,
- uploadFields: false,
-
- text: RLANG.drop_file_here,
- atext: RLANG.or_choose
-
- }, options);
-
- this.$el = $(el);
- }
-
- // Functionality
- Construct.prototype = {
- init: function()
- {
- if (navigator.userAgent.search("MSIE") === -1)
- {
- this.droparea = $('
');
- this.dropareabox = $('' + this.opts.text + '
');
- this.dropalternative = $('' + this.opts.atext + '
');
-
- this.droparea.append(this.dropareabox);
-
- this.$el.before(this.droparea);
- this.$el.before(this.dropalternative);
-
- // drag over
- this.dropareabox.bind('dragover', $.proxy(function() { return this.ondrag(); }, this));
-
- // drag leave
- this.dropareabox.bind('dragleave', $.proxy(function() { return this.ondragleave(); }, this));
-
- var uploadProgress = $.proxy(function(e)
- {
- var percent = parseInt(e.loaded / e.total * 100, 10);
- this.dropareabox.text('Loading ' + percent + '%');
-
- }, this);
-
- var xhr = jQuery.ajaxSettings.xhr();
-
- if (xhr.upload)
- {
- xhr.upload.addEventListener('progress', uploadProgress, false);
- }
-
- var provider = function () { return xhr; };
-
- // drop
- this.dropareabox.get(0).ondrop = $.proxy(function(event)
- {
- event.preventDefault();
-
- this.dropareabox.removeClass('hover').addClass('drop');
-
- var file = event.dataTransfer.files[0];
- var fd = new FormData();
-
- // append hidden fields
- if (this.opts.uploadFields !== false && typeof this.opts.uploadFields === 'object')
- {
- $.each(this.opts.uploadFields, $.proxy(function(k,v)
- {
- if (v.toString().indexOf('#') === 0)
- {
- v = $(v).val();
- }
-
- fd.append(k, v);
-
- }, this));
- }
-
- // append file data
- fd.append('file', file);
-
- $.ajax({
- url: this.opts.url,
- dataType: 'html',
- data: fd,
- xhr: provider,
- cache: false,
- contentType: false,
- processData: false,
- type: 'POST',
- success: $.proxy(function(data)
- {
- var json = $.parseJSON(data);
-
- if (typeof json.error == 'undefined')
- {
- this.opts.success(json);
- }
- else
- {
- this.opts.error(this, json);
- this.opts.success(false);
- }
-
- }, this)
- });
-
-
- }, this);
- }
- },
- ondrag: function()
- {
- this.dropareabox.addClass('hover');
- return false;
- },
- ondragleave: function()
- {
- this.dropareabox.removeClass('hover');
- return false;
- }
- };
-
-})(jQuery);
-
-
-
-// Define: Linkify plugin from stackoverflow
-(function($){
-
- "use strict";
-
- var protocol = 'http://';
- var url1 = /(^|<|\s)(www\..+?\..+?)(\s|>|$)/g,
- url2 = /(^|<|\s)(((https?|ftp):\/\/|mailto:).+?)(\s|>|$)/g,
-
- linkifyThis = function ()
- {
- var childNodes = this.childNodes,
- i = childNodes.length;
- while(i--)
- {
- var n = childNodes[i];
- if (n.nodeType === 3)
- {
- var html = n.nodeValue;
- if (html)
- {
- html = html.replace(/&/g, '&')
- .replace(//g, '>')
- .replace(url1, '$1$2 $3')
- .replace(url2, '$1$2 $5');
-
- $(n).after(html).remove();
- }
- }
- else if (n.nodeType === 1 && !/^(a|button|textarea)$/i.test(n.tagName))
- {
- linkifyThis.call(n);
- }
- }
- };
-
- $.fn.linkify = function ()
- {
- this.each(linkifyThis);
- };
-
-})(jQuery);
-
-
-/* jQuery plugin textselect
- * version: 0.9
- * author: Josef Moravec, josef.moravec@gmail.com
- * updated: Imperavi Inc.
- *
- */
-eval(function(p,a,c,k,e,d){e=function(c){return(c35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--){d[e(c)]=k[c]||e(c)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('(5($){$.1.4.7={t:5(0,v){$(2).0("8",c);$(2).0("r",0);$(2).l(\'g\',$.1.4.7.b)},u:5(0){$(2).w(\'g\',$.1.4.7.b)},b:5(1){9 0=$(2).0("r");9 3=$.1.4.7.f(0).h();6(3!=\'\'){$(2).0("8",x);1.j="7";1.3=3;$.1.i.m(2,k)}},f:5(0){9 3=\'\';6(q.e){3=q.e()}o 6(d.e){3=d.e()}o 6(d.p){3=d.p.B().3}A 3}};$.1.4.a={t:5(0,v){$(2).0("n",0);$(2).0("8",c);$(2).l(\'g\',$.1.4.a.b);$(2).l(\'D\',$.1.4.a.s)},u:5(0){$(2).w(\'g\',$.1.4.a.b)},b:5(1){6($(2).0("8")){9 0=$(2).0("n");9 3=$.1.4.7.f(0).h();6(3==\'\'){$(2).0("8",c);1.j="a";$.1.i.m(2,k)}}},s:5(1){6($(2).0("8")){9 0=$(2).0("n");9 3=$.1.4.7.f(0).h();6((1.y=z)&&(3==\'\')){$(2).0("8",c);1.j="a";$.1.i.m(2,k)}}}}})(C);',40,40,'data|event|this|text|special|function|if|textselect|textselected|var|textunselect|handler|false|rdocument|getSelection|getSelectedText|mouseup|toString|handle|type|arguments|bind|apply|rttt|else|selection|rwindow|ttt|handlerKey|setup|teardown|namespaces|unbind|true|keyCode|27|return|createRange|jQuery|keyup'.split('|'),0,{}))
\ No newline at end of file
diff --git a/app/assets/javascripts/bootstrap.js b/app/assets/javascripts/bootstrap.js
new file mode 100644
index 0000000..3f2bca0
--- /dev/null
+++ b/app/assets/javascripts/bootstrap.js
@@ -0,0 +1,13 @@
+//= require bootstrap/affix
+//= require bootstrap/alert
+//= require bootstrap/button
+//= require bootstrap/carousel
+
+//= require bootstrap/dropdown
+//= require bootstrap/tab
+//= require bootstrap/transition
+//= require bootstrap/scrollspy
+//= require bootstrap/modal
+//= require bootstrap/tooltip
+//= require bootstrap/popover
+//= require bootstrap/datetimepicker
diff --git a/app/assets/javascripts/bootstrap/affix.js b/app/assets/javascripts/bootstrap/affix.js
new file mode 100644
index 0000000..05c909e
--- /dev/null
+++ b/app/assets/javascripts/bootstrap/affix.js
@@ -0,0 +1,137 @@
+/* ========================================================================
+ * Bootstrap: affix.js v3.1.1
+ * http://getbootstrap.com/javascript/#affix
+ * ========================================================================
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+ 'use strict';
+
+ // AFFIX CLASS DEFINITION
+ // ======================
+
+ var Affix = function (element, options) {
+ this.options = $.extend({}, Affix.DEFAULTS, options)
+ this.$window = $(window)
+ .on('scroll.bs.affix.data-api', $.proxy(this.checkPosition, this))
+ .on('click.bs.affix.data-api', $.proxy(this.checkPositionWithEventLoop, this))
+
+ this.$element = $(element)
+ this.affixed =
+ this.unpin =
+ this.pinnedOffset = null
+
+ this.checkPosition()
+ }
+
+ Affix.RESET = 'affix affix-top affix-bottom'
+
+ Affix.DEFAULTS = {
+ offset: 0
+ }
+
+ Affix.prototype.getPinnedOffset = function () {
+ if (this.pinnedOffset) return this.pinnedOffset
+ this.$element.removeClass(Affix.RESET).addClass('affix')
+ var scrollTop = this.$window.scrollTop()
+ var position = this.$element.offset()
+ return (this.pinnedOffset = position.top - scrollTop)
+ }
+
+ Affix.prototype.checkPositionWithEventLoop = function () {
+ setTimeout($.proxy(this.checkPosition, this), 1)
+ }
+
+ Affix.prototype.checkPosition = function () {
+ if (!this.$element.is(':visible')) return
+
+ var scrollHeight = $(document).height()
+ var scrollTop = this.$window.scrollTop()
+ var position = this.$element.offset()
+ var offset = this.options.offset
+ var offsetTop = offset.top
+ var offsetBottom = offset.bottom
+
+ if (this.affixed == 'top') position.top += scrollTop
+
+ if (typeof offset != 'object') offsetBottom = offsetTop = offset
+ if (typeof offsetTop == 'function') offsetTop = offset.top(this.$element)
+ if (typeof offsetBottom == 'function') offsetBottom = offset.bottom(this.$element)
+
+ var affix = this.unpin != null && (scrollTop + this.unpin <= position.top) ? false :
+ offsetBottom != null && (position.top + this.$element.height() >= scrollHeight - offsetBottom) ? 'bottom' :
+ offsetTop != null && (scrollTop <= offsetTop) ? 'top' : false
+
+ if (this.affixed === affix) return
+ if (this.unpin) this.$element.css('top', '')
+
+ var affixType = 'affix' + (affix ? '-' + affix : '')
+ var e = $.Event(affixType + '.bs.affix')
+
+ this.$element.trigger(e)
+
+ if (e.isDefaultPrevented()) return
+
+ this.affixed = affix
+ this.unpin = affix == 'bottom' ? this.getPinnedOffset() : null
+
+ this.$element
+ .removeClass(Affix.RESET)
+ .addClass(affixType)
+ .trigger($.Event(affixType.replace('affix', 'affixed')))
+
+ if (affix == 'bottom') {
+ this.$element.offset({ top: scrollHeight - offsetBottom - this.$element.height() })
+ }
+ }
+
+
+ // AFFIX PLUGIN DEFINITION
+ // =======================
+
+ var old = $.fn.affix
+
+ $.fn.affix = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.affix')
+ var options = typeof option == 'object' && option
+
+ if (!data) $this.data('bs.affix', (data = new Affix(this, options)))
+ if (typeof option == 'string') data[option]()
+ })
+ }
+
+ $.fn.affix.Constructor = Affix
+
+
+ // AFFIX NO CONFLICT
+ // =================
+
+ $.fn.affix.noConflict = function () {
+ $.fn.affix = old
+ return this
+ }
+
+
+ // AFFIX DATA-API
+ // ==============
+
+ $(window).on('load', function () {
+ $('[data-spy="affix"]').each(function () {
+ var $spy = $(this)
+ var data = $spy.data()
+
+ data.offset = data.offset || {}
+
+ if (data.offsetBottom) data.offset.bottom = data.offsetBottom
+ if (data.offsetTop) data.offset.top = data.offsetTop
+
+ $spy.affix(data)
+ })
+ })
+
+}(jQuery);
diff --git a/app/assets/javascripts/bootstrap/alert.js b/app/assets/javascripts/bootstrap/alert.js
new file mode 100644
index 0000000..516fe4f
--- /dev/null
+++ b/app/assets/javascripts/bootstrap/alert.js
@@ -0,0 +1,88 @@
+/* ========================================================================
+ * Bootstrap: alert.js v3.1.1
+ * http://getbootstrap.com/javascript/#alerts
+ * ========================================================================
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+ 'use strict';
+
+ // ALERT CLASS DEFINITION
+ // ======================
+
+ var dismiss = '[data-dismiss="alert"]'
+ var Alert = function (el) {
+ $(el).on('click', dismiss, this.close)
+ }
+
+ Alert.prototype.close = function (e) {
+ var $this = $(this)
+ var selector = $this.attr('data-target')
+
+ if (!selector) {
+ selector = $this.attr('href')
+ selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
+ }
+
+ var $parent = $(selector)
+
+ if (e) e.preventDefault()
+
+ if (!$parent.length) {
+ $parent = $this.hasClass('alert') ? $this : $this.parent()
+ }
+
+ $parent.trigger(e = $.Event('close.bs.alert'))
+
+ if (e.isDefaultPrevented()) return
+
+ $parent.removeClass('in')
+
+ function removeElement() {
+ $parent.trigger('closed.bs.alert').remove()
+ }
+
+ $.support.transition && $parent.hasClass('fade') ?
+ $parent
+ .one($.support.transition.end, removeElement)
+ .emulateTransitionEnd(150) :
+ removeElement()
+ }
+
+
+ // ALERT PLUGIN DEFINITION
+ // =======================
+
+ var old = $.fn.alert
+
+ $.fn.alert = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.alert')
+
+ if (!data) $this.data('bs.alert', (data = new Alert(this)))
+ if (typeof option == 'string') data[option].call($this)
+ })
+ }
+
+ $.fn.alert.Constructor = Alert
+
+
+ // ALERT NO CONFLICT
+ // =================
+
+ $.fn.alert.noConflict = function () {
+ $.fn.alert = old
+ return this
+ }
+
+
+ // ALERT DATA-API
+ // ==============
+
+ $(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close)
+
+}(jQuery);
diff --git a/app/assets/javascripts/bootstrap/button.js b/app/assets/javascripts/bootstrap/button.js
new file mode 100644
index 0000000..f4d8d8b
--- /dev/null
+++ b/app/assets/javascripts/bootstrap/button.js
@@ -0,0 +1,107 @@
+/* ========================================================================
+ * Bootstrap: button.js v3.1.1
+ * http://getbootstrap.com/javascript/#buttons
+ * ========================================================================
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+ 'use strict';
+
+ // BUTTON PUBLIC CLASS DEFINITION
+ // ==============================
+
+ var Button = function (element, options) {
+ this.$element = $(element)
+ this.options = $.extend({}, Button.DEFAULTS, options)
+ this.isLoading = false
+ }
+
+ Button.DEFAULTS = {
+ loadingText: 'loading...'
+ }
+
+ Button.prototype.setState = function (state) {
+ var d = 'disabled'
+ var $el = this.$element
+ var val = $el.is('input') ? 'val' : 'html'
+ var data = $el.data()
+
+ state = state + 'Text'
+
+ if (!data.resetText) $el.data('resetText', $el[val]())
+
+ $el[val](data[state] || this.options[state])
+
+ // push to event loop to allow forms to submit
+ setTimeout($.proxy(function () {
+ if (state == 'loadingText') {
+ this.isLoading = true
+ $el.addClass(d).attr(d, d)
+ } else if (this.isLoading) {
+ this.isLoading = false
+ $el.removeClass(d).removeAttr(d)
+ }
+ }, this), 0)
+ }
+
+ Button.prototype.toggle = function () {
+ var changed = true
+ var $parent = this.$element.closest('[data-toggle="buttons"]')
+
+ if ($parent.length) {
+ var $input = this.$element.find('input')
+ if ($input.prop('type') == 'radio') {
+ if ($input.prop('checked') && this.$element.hasClass('active')) changed = false
+ else $parent.find('.active').removeClass('active')
+ }
+ if (changed) $input.prop('checked', !this.$element.hasClass('active')).trigger('change')
+ }
+
+ if (changed) this.$element.toggleClass('active')
+ }
+
+
+ // BUTTON PLUGIN DEFINITION
+ // ========================
+
+ var old = $.fn.button
+
+ $.fn.button = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.button')
+ var options = typeof option == 'object' && option
+
+ if (!data) $this.data('bs.button', (data = new Button(this, options)))
+
+ if (option == 'toggle') data.toggle()
+ else if (option) data.setState(option)
+ })
+ }
+
+ $.fn.button.Constructor = Button
+
+
+ // BUTTON NO CONFLICT
+ // ==================
+
+ $.fn.button.noConflict = function () {
+ $.fn.button = old
+ return this
+ }
+
+
+ // BUTTON DATA-API
+ // ===============
+
+ $(document).on('click.bs.button.data-api', '[data-toggle^=button]', function (e) {
+ var $btn = $(e.target)
+ if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn')
+ $btn.button('toggle')
+ e.preventDefault()
+ })
+
+}(jQuery);
diff --git a/app/assets/javascripts/bootstrap/carousel.js b/app/assets/javascripts/bootstrap/carousel.js
new file mode 100644
index 0000000..19e9af1
--- /dev/null
+++ b/app/assets/javascripts/bootstrap/carousel.js
@@ -0,0 +1,205 @@
+/* ========================================================================
+ * Bootstrap: carousel.js v3.1.1
+ * http://getbootstrap.com/javascript/#carousel
+ * ========================================================================
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+ 'use strict';
+
+ // CAROUSEL CLASS DEFINITION
+ // =========================
+
+ var Carousel = function (element, options) {
+ this.$element = $(element)
+ this.$indicators = this.$element.find('.carousel-indicators')
+ this.options = options
+ this.paused =
+ this.sliding =
+ this.interval =
+ this.$active =
+ this.$items = null
+
+ this.options.pause == 'hover' && this.$element
+ .on('mouseenter', $.proxy(this.pause, this))
+ .on('mouseleave', $.proxy(this.cycle, this))
+ }
+
+ Carousel.DEFAULTS = {
+ interval: 5000,
+ pause: 'hover',
+ wrap: true
+ }
+
+ Carousel.prototype.cycle = function (e) {
+ e || (this.paused = false)
+
+ this.interval && clearInterval(this.interval)
+
+ this.options.interval
+ && !this.paused
+ && (this.interval = setInterval($.proxy(this.next, this), this.options.interval))
+
+ return this
+ }
+
+ Carousel.prototype.getActiveIndex = function () {
+ this.$active = this.$element.find('.item.active')
+ this.$items = this.$active.parent().children()
+
+ return this.$items.index(this.$active)
+ }
+
+ Carousel.prototype.to = function (pos) {
+ var that = this
+ var activeIndex = this.getActiveIndex()
+
+ if (pos > (this.$items.length - 1) || pos < 0) return
+
+ if (this.sliding) return this.$element.one('slid.bs.carousel', function () { that.to(pos) })
+ if (activeIndex == pos) return this.pause().cycle()
+
+ return this.slide(pos > activeIndex ? 'next' : 'prev', $(this.$items[pos]))
+ }
+
+ Carousel.prototype.pause = function (e) {
+ e || (this.paused = true)
+
+ if (this.$element.find('.next, .prev').length && $.support.transition) {
+ this.$element.trigger($.support.transition.end)
+ this.cycle(true)
+ }
+
+ this.interval = clearInterval(this.interval)
+
+ return this
+ }
+
+ Carousel.prototype.next = function () {
+ if (this.sliding) return
+ return this.slide('next')
+ }
+
+ Carousel.prototype.prev = function () {
+ if (this.sliding) return
+ return this.slide('prev')
+ }
+
+ Carousel.prototype.slide = function (type, next) {
+ var $active = this.$element.find('.item.active')
+ var $next = next || $active[type]()
+ var isCycling = this.interval
+ var direction = type == 'next' ? 'left' : 'right'
+ var fallback = type == 'next' ? 'first' : 'last'
+ var that = this
+
+ if (!$next.length) {
+ if (!this.options.wrap) return
+ $next = this.$element.find('.item')[fallback]()
+ }
+
+ if ($next.hasClass('active')) return this.sliding = false
+
+ var e = $.Event('slide.bs.carousel', { relatedTarget: $next[0], direction: direction })
+ this.$element.trigger(e)
+ if (e.isDefaultPrevented()) return
+
+ this.sliding = true
+
+ isCycling && this.pause()
+
+ if (this.$indicators.length) {
+ this.$indicators.find('.active').removeClass('active')
+ this.$element.one('slid.bs.carousel', function () {
+ var $nextIndicator = $(that.$indicators.children()[that.getActiveIndex()])
+ $nextIndicator && $nextIndicator.addClass('active')
+ })
+ }
+
+ if ($.support.transition && this.$element.hasClass('slide')) {
+ $next.addClass(type)
+ $next[0].offsetWidth // force reflow
+ $active.addClass(direction)
+ $next.addClass(direction)
+ $active
+ .one($.support.transition.end, function () {
+ $next.removeClass([type, direction].join(' ')).addClass('active')
+ $active.removeClass(['active', direction].join(' '))
+ that.sliding = false
+ setTimeout(function () { that.$element.trigger('slid.bs.carousel') }, 0)
+ })
+ .emulateTransitionEnd($active.css('transition-duration').slice(0, -1) * 1000)
+ } else {
+ $active.removeClass('active')
+ $next.addClass('active')
+ this.sliding = false
+ this.$element.trigger('slid.bs.carousel')
+ }
+
+ isCycling && this.cycle()
+
+ return this
+ }
+
+
+ // CAROUSEL PLUGIN DEFINITION
+ // ==========================
+
+ var old = $.fn.carousel
+
+ $.fn.carousel = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.carousel')
+ var options = $.extend({}, Carousel.DEFAULTS, $this.data(), typeof option == 'object' && option)
+ var action = typeof option == 'string' ? option : options.slide
+
+ if (!data) $this.data('bs.carousel', (data = new Carousel(this, options)))
+ if (typeof option == 'number') data.to(option)
+ else if (action) data[action]()
+ else if (options.interval) data.pause().cycle()
+ })
+ }
+
+ $.fn.carousel.Constructor = Carousel
+
+
+ // CAROUSEL NO CONFLICT
+ // ====================
+
+ $.fn.carousel.noConflict = function () {
+ $.fn.carousel = old
+ return this
+ }
+
+
+ // CAROUSEL DATA-API
+ // =================
+
+ $(document).on('click.bs.carousel.data-api', '[data-slide], [data-slide-to]', function (e) {
+ var $this = $(this), href
+ var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
+ var options = $.extend({}, $target.data(), $this.data())
+ var slideIndex = $this.attr('data-slide-to')
+ if (slideIndex) options.interval = false
+
+ $target.carousel(options)
+
+ if (slideIndex = $this.attr('data-slide-to')) {
+ $target.data('bs.carousel').to(slideIndex)
+ }
+
+ e.preventDefault()
+ })
+
+ $(window).on('load', function () {
+ $('[data-ride="carousel"]').each(function () {
+ var $carousel = $(this)
+ $carousel.carousel($carousel.data())
+ })
+ })
+
+}(jQuery);
diff --git a/app/assets/javascripts/bootstrap/collapse.js b/app/assets/javascripts/bootstrap/collapse.js
new file mode 100644
index 0000000..7130282
--- /dev/null
+++ b/app/assets/javascripts/bootstrap/collapse.js
@@ -0,0 +1,170 @@
+/* ========================================================================
+ * Bootstrap: collapse.js v3.1.1
+ * http://getbootstrap.com/javascript/#collapse
+ * ========================================================================
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+ 'use strict';
+
+ // COLLAPSE PUBLIC CLASS DEFINITION
+ // ================================
+
+ var Collapse = function (element, options) {
+ this.$element = $(element)
+ this.options = $.extend({}, Collapse.DEFAULTS, options)
+ this.transitioning = null
+
+ if (this.options.parent) this.$parent = $(this.options.parent)
+ if (this.options.toggle) this.toggle()
+ }
+
+ Collapse.DEFAULTS = {
+ toggle: true
+ }
+
+ Collapse.prototype.dimension = function () {
+ var hasWidth = this.$element.hasClass('width')
+ return hasWidth ? 'width' : 'height'
+ }
+
+ Collapse.prototype.show = function () {
+ if (this.transitioning || this.$element.hasClass('in')) return
+
+ var startEvent = $.Event('show.bs.collapse')
+ this.$element.trigger(startEvent)
+ if (startEvent.isDefaultPrevented()) return
+
+ var actives = this.$parent && this.$parent.find('> .panel > .in')
+
+ if (actives && actives.length) {
+ var hasData = actives.data('bs.collapse')
+ if (hasData && hasData.transitioning) return
+ actives.collapse('hide')
+ hasData || actives.data('bs.collapse', null)
+ }
+
+ var dimension = this.dimension()
+
+ this.$element
+ .removeClass('collapse')
+ .addClass('collapsing')
+ [dimension](0)
+
+ this.transitioning = 1
+
+ var complete = function () {
+ this.$element
+ .removeClass('collapsing')
+ .addClass('collapse in')
+ [dimension]('auto')
+ this.transitioning = 0
+ this.$element.trigger('shown.bs.collapse')
+ }
+
+ if (!$.support.transition) return complete.call(this)
+
+ var scrollSize = $.camelCase(['scroll', dimension].join('-'))
+
+ this.$element
+ .one($.support.transition.end, $.proxy(complete, this))
+ .emulateTransitionEnd(350)
+ [dimension](this.$element[0][scrollSize])
+ }
+
+ Collapse.prototype.hide = function () {
+ if (this.transitioning || !this.$element.hasClass('in')) return
+
+ var startEvent = $.Event('hide.bs.collapse')
+ this.$element.trigger(startEvent)
+ if (startEvent.isDefaultPrevented()) return
+
+ var dimension = this.dimension()
+
+ this.$element
+ [dimension](this.$element[dimension]())
+ [0].offsetHeight
+
+ this.$element
+ .addClass('collapsing')
+ .removeClass('collapse')
+ .removeClass('in')
+
+ this.transitioning = 1
+
+ var complete = function () {
+ this.transitioning = 0
+ this.$element
+ .trigger('hidden.bs.collapse')
+ .removeClass('collapsing')
+ .addClass('collapse')
+ }
+
+ if (!$.support.transition) return complete.call(this)
+
+ this.$element
+ [dimension](0)
+ .one($.support.transition.end, $.proxy(complete, this))
+ .emulateTransitionEnd(350)
+ }
+
+ Collapse.prototype.toggle = function () {
+ this[this.$element.hasClass('in') ? 'hide' : 'show']()
+ }
+
+
+ // COLLAPSE PLUGIN DEFINITION
+ // ==========================
+
+ var old = $.fn.collapse
+
+ $.fn.collapse = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.collapse')
+ var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option)
+
+ if (!data && options.toggle && option == 'show') option = !option
+ if (!data) $this.data('bs.collapse', (data = new Collapse(this, options)))
+ if (typeof option == 'string') data[option]()
+ })
+ }
+
+ $.fn.collapse.Constructor = Collapse
+
+
+ // COLLAPSE NO CONFLICT
+ // ====================
+
+ $.fn.collapse.noConflict = function () {
+ $.fn.collapse = old
+ return this
+ }
+
+
+ // COLLAPSE DATA-API
+ // =================
+
+ $(document).on('click.bs.collapse.data-api', '[data-toggle=collapse]', function (e) {
+ var $this = $(this), href
+ var target = $this.attr('data-target')
+ || e.preventDefault()
+ || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7
+ var $target = $(target)
+ var data = $target.data('bs.collapse')
+ var option = data ? 'toggle' : $this.data()
+ var parent = $this.attr('data-parent')
+ var $parent = parent && $(parent)
+
+ if (!data || !data.transitioning) {
+ if ($parent) $parent.find('[data-toggle=collapse][data-parent="' + parent + '"]').not($this).addClass('collapsed')
+ $this[$target.hasClass('in') ? 'addClass' : 'removeClass']('collapsed')
+ }
+
+ $target.collapse(option)
+ })
+
+}(jQuery);
diff --git a/app/assets/javascripts/bootstrap/datetimepicker.js b/app/assets/javascripts/bootstrap/datetimepicker.js
new file mode 100755
index 0000000..7465e36
--- /dev/null
+++ b/app/assets/javascripts/bootstrap/datetimepicker.js
@@ -0,0 +1,3652 @@
+//! moment.js
+//! version : 2.5.1
+//! authors : Tim Wood, Iskren Chernev, Moment.js contributors
+//! license : MIT
+//! momentjs.com
+
+(function (undefined) {
+
+ /************************************
+ Constants
+ ************************************/
+
+ var moment,
+ VERSION = "2.5.1",
+ global = this,
+ round = Math.round,
+ i,
+
+ YEAR = 0,
+ MONTH = 1,
+ DATE = 2,
+ HOUR = 3,
+ MINUTE = 4,
+ SECOND = 5,
+ MILLISECOND = 6,
+
+ // internal storage for language config files
+ languages = {},
+
+ // moment internal properties
+ momentProperties = {
+ _isAMomentObject: null,
+ _i : null,
+ _f : null,
+ _l : null,
+ _strict : null,
+ _isUTC : null,
+ _offset : null, // optional. Combine with _isUTC
+ _pf : null,
+ _lang : null // optional
+ },
+
+ // check for nodeJS
+ hasModule = (typeof module !== 'undefined' && module.exports && typeof require !== 'undefined'),
+
+ // ASP.NET json date format regex
+ aspNetJsonRegex = /^\/?Date\((\-?\d+)/i,
+ aspNetTimeSpanJsonRegex = /(\-)?(?:(\d*)\.)?(\d+)\:(\d+)(?:\:(\d+)\.?(\d{3})?)?/,
+
+ // from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html
+ // somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere
+ isoDurationRegex = /^(-)?P(?:(?:([0-9,.]*)Y)?(?:([0-9,.]*)M)?(?:([0-9,.]*)D)?(?:T(?:([0-9,.]*)H)?(?:([0-9,.]*)M)?(?:([0-9,.]*)S)?)?|([0-9,.]*)W)$/,
+
+ // format tokens
+ formattingTokens = /(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|mm?|ss?|S{1,4}|X|zz?|ZZ?|.)/g,
+ localFormattingTokens = /(\[[^\[]*\])|(\\)?(LT|LL?L?L?|l{1,4})/g,
+
+ // parsing token regexes
+ parseTokenOneOrTwoDigits = /\d\d?/, // 0 - 99
+ parseTokenOneToThreeDigits = /\d{1,3}/, // 0 - 999
+ parseTokenOneToFourDigits = /\d{1,4}/, // 0 - 9999
+ parseTokenOneToSixDigits = /[+\-]?\d{1,6}/, // -999,999 - 999,999
+ parseTokenDigits = /\d+/, // nonzero number of digits
+ parseTokenWord = /[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i, // any word (or two) characters or numbers including two/three word month in arabic.
+ parseTokenTimezone = /Z|[\+\-]\d\d:?\d\d/gi, // +00:00 -00:00 +0000 -0000 or Z
+ parseTokenT = /T/i, // T (ISO separator)
+ parseTokenTimestampMs = /[\+\-]?\d+(\.\d{1,3})?/, // 123456789 123456789.123
+
+ //strict parsing regexes
+ parseTokenOneDigit = /\d/, // 0 - 9
+ parseTokenTwoDigits = /\d\d/, // 00 - 99
+ parseTokenThreeDigits = /\d{3}/, // 000 - 999
+ parseTokenFourDigits = /\d{4}/, // 0000 - 9999
+ parseTokenSixDigits = /[+-]?\d{6}/, // -999,999 - 999,999
+ parseTokenSignedNumber = /[+-]?\d+/, // -inf - inf
+
+ // iso 8601 regex
+ // 0000-00-00 0000-W00 or 0000-W00-0 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000 or +00)
+ isoRegex = /^\s*(?:[+-]\d{6}|\d{4})-(?:(\d\d-\d\d)|(W\d\d$)|(W\d\d-\d)|(\d\d\d))((T| )(\d\d(:\d\d(:\d\d(\.\d+)?)?)?)?([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,
+
+ isoFormat = 'YYYY-MM-DDTHH:mm:ssZ',
+
+ isoDates = [
+ ['YYYYYY-MM-DD', /[+-]\d{6}-\d{2}-\d{2}/],
+ ['YYYY-MM-DD', /\d{4}-\d{2}-\d{2}/],
+ ['GGGG-[W]WW-E', /\d{4}-W\d{2}-\d/],
+ ['GGGG-[W]WW', /\d{4}-W\d{2}/],
+ ['YYYY-DDD', /\d{4}-\d{3}/]
+ ],
+
+ // iso time formats and regexes
+ isoTimes = [
+ ['HH:mm:ss.SSSS', /(T| )\d\d:\d\d:\d\d\.\d{1,3}/],
+ ['HH:mm:ss', /(T| )\d\d:\d\d:\d\d/],
+ ['HH:mm', /(T| )\d\d:\d\d/],
+ ['HH', /(T| )\d\d/]
+ ],
+
+ // timezone chunker "+10:00" > ["10", "00"] or "-1530" > ["-15", "30"]
+ parseTimezoneChunker = /([\+\-]|\d\d)/gi,
+
+ // getter and setter names
+ proxyGettersAndSetters = 'Date|Hours|Minutes|Seconds|Milliseconds'.split('|'),
+ unitMillisecondFactors = {
+ 'Milliseconds' : 1,
+ 'Seconds' : 1e3,
+ 'Minutes' : 6e4,
+ 'Hours' : 36e5,
+ 'Days' : 864e5,
+ 'Months' : 2592e6,
+ 'Years' : 31536e6
+ },
+
+ unitAliases = {
+ ms : 'millisecond',
+ s : 'second',
+ m : 'minute',
+ h : 'hour',
+ d : 'day',
+ D : 'date',
+ w : 'week',
+ W : 'isoWeek',
+ M : 'month',
+ y : 'year',
+ DDD : 'dayOfYear',
+ e : 'weekday',
+ E : 'isoWeekday',
+ gg: 'weekYear',
+ GG: 'isoWeekYear'
+ },
+
+ camelFunctions = {
+ dayofyear : 'dayOfYear',
+ isoweekday : 'isoWeekday',
+ isoweek : 'isoWeek',
+ weekyear : 'weekYear',
+ isoweekyear : 'isoWeekYear'
+ },
+
+ // format function strings
+ formatFunctions = {},
+
+ // tokens to ordinalize and pad
+ ordinalizeTokens = 'DDD w W M D d'.split(' '),
+ paddedTokens = 'M D H h m s w W'.split(' '),
+
+ formatTokenFunctions = {
+ M : function () {
+ return this.month() + 1;
+ },
+ MMM : function (format) {
+ return this.lang().monthsShort(this, format);
+ },
+ MMMM : function (format) {
+ return this.lang().months(this, format);
+ },
+ D : function () {
+ return this.date();
+ },
+ DDD : function () {
+ return this.dayOfYear();
+ },
+ d : function () {
+ return this.day();
+ },
+ dd : function (format) {
+ return this.lang().weekdaysMin(this, format);
+ },
+ ddd : function (format) {
+ return this.lang().weekdaysShort(this, format);
+ },
+ dddd : function (format) {
+ return this.lang().weekdays(this, format);
+ },
+ w : function () {
+ return this.week();
+ },
+ W : function () {
+ return this.isoWeek();
+ },
+ YY : function () {
+ return leftZeroFill(this.year() % 100, 2);
+ },
+ YYYY : function () {
+ return leftZeroFill(this.year(), 4);
+ },
+ YYYYY : function () {
+ return leftZeroFill(this.year(), 5);
+ },
+ YYYYYY : function () {
+ var y = this.year(), sign = y >= 0 ? '+' : '-';
+ return sign + leftZeroFill(Math.abs(y), 6);
+ },
+ gg : function () {
+ return leftZeroFill(this.weekYear() % 100, 2);
+ },
+ gggg : function () {
+ return leftZeroFill(this.weekYear(), 4);
+ },
+ ggggg : function () {
+ return leftZeroFill(this.weekYear(), 5);
+ },
+ GG : function () {
+ return leftZeroFill(this.isoWeekYear() % 100, 2);
+ },
+ GGGG : function () {
+ return leftZeroFill(this.isoWeekYear(), 4);
+ },
+ GGGGG : function () {
+ return leftZeroFill(this.isoWeekYear(), 5);
+ },
+ e : function () {
+ return this.weekday();
+ },
+ E : function () {
+ return this.isoWeekday();
+ },
+ a : function () {
+ return this.lang().meridiem(this.hours(), this.minutes(), true);
+ },
+ A : function () {
+ return this.lang().meridiem(this.hours(), this.minutes(), false);
+ },
+ H : function () {
+ return this.hours();
+ },
+ h : function () {
+ return this.hours() % 12 || 12;
+ },
+ m : function () {
+ return this.minutes();
+ },
+ s : function () {
+ return this.seconds();
+ },
+ S : function () {
+ return toInt(this.milliseconds() / 100);
+ },
+ SS : function () {
+ return leftZeroFill(toInt(this.milliseconds() / 10), 2);
+ },
+ SSS : function () {
+ return leftZeroFill(this.milliseconds(), 3);
+ },
+ SSSS : function () {
+ return leftZeroFill(this.milliseconds(), 3);
+ },
+ Z : function () {
+ var a = -this.zone(),
+ b = "+";
+ if (a < 0) {
+ a = -a;
+ b = "-";
+ }
+ return b + leftZeroFill(toInt(a / 60), 2) + ":" + leftZeroFill(toInt(a) % 60, 2);
+ },
+ ZZ : function () {
+ var a = -this.zone(),
+ b = "+";
+ if (a < 0) {
+ a = -a;
+ b = "-";
+ }
+ return b + leftZeroFill(toInt(a / 60), 2) + leftZeroFill(toInt(a) % 60, 2);
+ },
+ z : function () {
+ return this.zoneAbbr();
+ },
+ zz : function () {
+ return this.zoneName();
+ },
+ X : function () {
+ return this.unix();
+ },
+ Q : function () {
+ return this.quarter();
+ }
+ },
+
+ lists = ['months', 'monthsShort', 'weekdays', 'weekdaysShort', 'weekdaysMin'];
+
+ function defaultParsingFlags() {
+ // We need to deep clone this object, and es5 standard is not very
+ // helpful.
+ return {
+ empty : false,
+ unusedTokens : [],
+ unusedInput : [],
+ overflow : -2,
+ charsLeftOver : 0,
+ nullInput : false,
+ invalidMonth : null,
+ invalidFormat : false,
+ userInvalidated : false,
+ iso: false
+ };
+ }
+
+ function padToken(func, count) {
+ return function (a) {
+ return leftZeroFill(func.call(this, a), count);
+ };
+ }
+ function ordinalizeToken(func, period) {
+ return function (a) {
+ return this.lang().ordinal(func.call(this, a), period);
+ };
+ }
+
+ while (ordinalizeTokens.length) {
+ i = ordinalizeTokens.pop();
+ formatTokenFunctions[i + 'o'] = ordinalizeToken(formatTokenFunctions[i], i);
+ }
+ while (paddedTokens.length) {
+ i = paddedTokens.pop();
+ formatTokenFunctions[i + i] = padToken(formatTokenFunctions[i], 2);
+ }
+ formatTokenFunctions.DDDD = padToken(formatTokenFunctions.DDD, 3);
+
+
+ /************************************
+ Constructors
+ ************************************/
+
+ function Language() {
+
+ }
+
+ // Moment prototype object
+ function Moment(config) {
+ checkOverflow(config);
+ extend(this, config);
+ }
+
+ // Duration Constructor
+ function Duration(duration) {
+ var normalizedInput = normalizeObjectUnits(duration),
+ years = normalizedInput.year || 0,
+ months = normalizedInput.month || 0,
+ weeks = normalizedInput.week || 0,
+ days = normalizedInput.day || 0,
+ hours = normalizedInput.hour || 0,
+ minutes = normalizedInput.minute || 0,
+ seconds = normalizedInput.second || 0,
+ milliseconds = normalizedInput.millisecond || 0;
+
+ // representation for dateAddRemove
+ this._milliseconds = +milliseconds +
+ seconds * 1e3 + // 1000
+ minutes * 6e4 + // 1000 * 60
+ hours * 36e5; // 1000 * 60 * 60
+ // Because of dateAddRemove treats 24 hours as different from a
+ // day when working around DST, we need to store them separately
+ this._days = +days +
+ weeks * 7;
+ // It is impossible translate months into days without knowing
+ // which months you are are talking about, so we have to store
+ // it separately.
+ this._months = +months +
+ years * 12;
+
+ this._data = {};
+
+ this._bubble();
+ }
+
+ /************************************
+ Helpers
+ ************************************/
+
+
+ function extend(a, b) {
+ for (var i in b) {
+ if (b.hasOwnProperty(i)) {
+ a[i] = b[i];
+ }
+ }
+
+ if (b.hasOwnProperty("toString")) {
+ a.toString = b.toString;
+ }
+
+ if (b.hasOwnProperty("valueOf")) {
+ a.valueOf = b.valueOf;
+ }
+
+ return a;
+ }
+
+ function cloneMoment(m) {
+ var result = {}, i;
+ for (i in m) {
+ if (m.hasOwnProperty(i) && momentProperties.hasOwnProperty(i)) {
+ result[i] = m[i];
+ }
+ }
+
+ return result;
+ }
+
+ function absRound(number) {
+ if (number < 0) {
+ return Math.ceil(number);
+ } else {
+ return Math.floor(number);
+ }
+ }
+
+ // left zero fill a number
+ // see http://jsperf.com/left-zero-filling for performance comparison
+ function leftZeroFill(number, targetLength, forceSign) {
+ var output = '' + Math.abs(number),
+ sign = number >= 0;
+
+ while (output.length < targetLength) {
+ output = '0' + output;
+ }
+ return (sign ? (forceSign ? '+' : '') : '-') + output;
+ }
+
+ // helper function for _.addTime and _.subtractTime
+ function addOrSubtractDurationFromMoment(mom, duration, isAdding, ignoreUpdateOffset) {
+ var milliseconds = duration._milliseconds,
+ days = duration._days,
+ months = duration._months,
+ minutes,
+ hours;
+
+ if (milliseconds) {
+ mom._d.setTime(+mom._d + milliseconds * isAdding);
+ }
+ // store the minutes and hours so we can restore them
+ if (days || months) {
+ minutes = mom.minute();
+ hours = mom.hour();
+ }
+ if (days) {
+ mom.date(mom.date() + days * isAdding);
+ }
+ if (months) {
+ mom.month(mom.month() + months * isAdding);
+ }
+ if (milliseconds && !ignoreUpdateOffset) {
+ moment.updateOffset(mom);
+ }
+ // restore the minutes and hours after possibly changing dst
+ if (days || months) {
+ mom.minute(minutes);
+ mom.hour(hours);
+ }
+ }
+
+ // check if is an array
+ function isArray(input) {
+ return Object.prototype.toString.call(input) === '[object Array]';
+ }
+
+ function isDate(input) {
+ return Object.prototype.toString.call(input) === '[object Date]' ||
+ input instanceof Date;
+ }
+
+ // compare two arrays, return the number of differences
+ function compareArrays(array1, array2, dontConvert) {
+ var len = Math.min(array1.length, array2.length),
+ lengthDiff = Math.abs(array1.length - array2.length),
+ diffs = 0,
+ i;
+ for (i = 0; i < len; i++) {
+ if ((dontConvert && array1[i] !== array2[i]) ||
+ (!dontConvert && toInt(array1[i]) !== toInt(array2[i]))) {
+ diffs++;
+ }
+ }
+ return diffs + lengthDiff;
+ }
+
+ function normalizeUnits(units) {
+ if (units) {
+ var lowered = units.toLowerCase().replace(/(.)s$/, '$1');
+ units = unitAliases[units] || camelFunctions[lowered] || lowered;
+ }
+ return units;
+ }
+
+ function normalizeObjectUnits(inputObject) {
+ var normalizedInput = {},
+ normalizedProp,
+ prop;
+
+ for (prop in inputObject) {
+ if (inputObject.hasOwnProperty(prop)) {
+ normalizedProp = normalizeUnits(prop);
+ if (normalizedProp) {
+ normalizedInput[normalizedProp] = inputObject[prop];
+ }
+ }
+ }
+
+ return normalizedInput;
+ }
+
+ function makeList(field) {
+ var count, setter;
+
+ if (field.indexOf('week') === 0) {
+ count = 7;
+ setter = 'day';
+ }
+ else if (field.indexOf('month') === 0) {
+ count = 12;
+ setter = 'month';
+ }
+ else {
+ return;
+ }
+
+ moment[field] = function (format, index) {
+ var i, getter,
+ method = moment.fn._lang[field],
+ results = [];
+
+ if (typeof format === 'number') {
+ index = format;
+ format = undefined;
+ }
+
+ getter = function (i) {
+ var m = moment().utc().set(setter, i);
+ return method.call(moment.fn._lang, m, format || '');
+ };
+
+ if (index != null) {
+ return getter(index);
+ }
+ else {
+ for (i = 0; i < count; i++) {
+ results.push(getter(i));
+ }
+ return results;
+ }
+ };
+ }
+
+ function toInt(argumentForCoercion) {
+ var coercedNumber = +argumentForCoercion,
+ value = 0;
+
+ if (coercedNumber !== 0 && isFinite(coercedNumber)) {
+ if (coercedNumber >= 0) {
+ value = Math.floor(coercedNumber);
+ } else {
+ value = Math.ceil(coercedNumber);
+ }
+ }
+
+ return value;
+ }
+
+ function daysInMonth(year, month) {
+ return new Date(Date.UTC(year, month + 1, 0)).getUTCDate();
+ }
+
+ function daysInYear(year) {
+ return isLeapYear(year) ? 366 : 365;
+ }
+
+ function isLeapYear(year) {
+ return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
+ }
+
+ function checkOverflow(m) {
+ var overflow;
+ if (m._a && m._pf.overflow === -2) {
+ overflow =
+ m._a[MONTH] < 0 || m._a[MONTH] > 11 ? MONTH :
+ m._a[DATE] < 1 || m._a[DATE] > daysInMonth(m._a[YEAR], m._a[MONTH]) ? DATE :
+ m._a[HOUR] < 0 || m._a[HOUR] > 23 ? HOUR :
+ m._a[MINUTE] < 0 || m._a[MINUTE] > 59 ? MINUTE :
+ m._a[SECOND] < 0 || m._a[SECOND] > 59 ? SECOND :
+ m._a[MILLISECOND] < 0 || m._a[MILLISECOND] > 999 ? MILLISECOND :
+ -1;
+
+ if (m._pf._overflowDayOfYear && (overflow < YEAR || overflow > DATE)) {
+ overflow = DATE;
+ }
+
+ m._pf.overflow = overflow;
+ }
+ }
+
+ function isValid(m) {
+ if (m._isValid == null) {
+ m._isValid = !isNaN(m._d.getTime()) &&
+ m._pf.overflow < 0 &&
+ !m._pf.empty &&
+ !m._pf.invalidMonth &&
+ !m._pf.nullInput &&
+ !m._pf.invalidFormat &&
+ !m._pf.userInvalidated;
+
+ if (m._strict) {
+ m._isValid = m._isValid &&
+ m._pf.charsLeftOver === 0 &&
+ m._pf.unusedTokens.length === 0;
+ }
+ }
+ return m._isValid;
+ }
+
+ function normalizeLanguage(key) {
+ return key ? key.toLowerCase().replace('_', '-') : key;
+ }
+
+ // Return a moment from input, that is local/utc/zone equivalent to model.
+ function makeAs(input, model) {
+ return model._isUTC ? moment(input).zone(model._offset || 0) :
+ moment(input).local();
+ }
+
+ /************************************
+ Languages
+ ************************************/
+
+
+ extend(Language.prototype, {
+
+ set : function (config) {
+ var prop, i;
+ for (i in config) {
+ prop = config[i];
+ if (typeof prop === 'function') {
+ this[i] = prop;
+ } else {
+ this['_' + i] = prop;
+ }
+ }
+ },
+
+ _months : "January_February_March_April_May_June_July_August_September_October_November_December".split("_"),
+ months : function (m) {
+ return this._months[m.month()];
+ },
+
+ _monthsShort : "Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),
+ monthsShort : function (m) {
+ return this._monthsShort[m.month()];
+ },
+
+ monthsParse : function (monthName) {
+ var i, mom, regex;
+
+ if (!this._monthsParse) {
+ this._monthsParse = [];
+ }
+
+ for (i = 0; i < 12; i++) {
+ // make the regex if we don't have it already
+ if (!this._monthsParse[i]) {
+ mom = moment.utc([2000, i]);
+ regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, '');
+ this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i');
+ }
+ // test the regex
+ if (this._monthsParse[i].test(monthName)) {
+ return i;
+ }
+ }
+ },
+
+ _weekdays : "Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),
+ weekdays : function (m) {
+ return this._weekdays[m.day()];
+ },
+
+ _weekdaysShort : "Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),
+ weekdaysShort : function (m) {
+ return this._weekdaysShort[m.day()];
+ },
+
+ _weekdaysMin : "Su_Mo_Tu_We_Th_Fr_Sa".split("_"),
+ weekdaysMin : function (m) {
+ return this._weekdaysMin[m.day()];
+ },
+
+ weekdaysParse : function (weekdayName) {
+ var i, mom, regex;
+
+ if (!this._weekdaysParse) {
+ this._weekdaysParse = [];
+ }
+
+ for (i = 0; i < 7; i++) {
+ // make the regex if we don't have it already
+ if (!this._weekdaysParse[i]) {
+ mom = moment([2000, 1]).day(i);
+ regex = '^' + this.weekdays(mom, '') + '|^' + this.weekdaysShort(mom, '') + '|^' + this.weekdaysMin(mom, '');
+ this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i');
+ }
+ // test the regex
+ if (this._weekdaysParse[i].test(weekdayName)) {
+ return i;
+ }
+ }
+ },
+
+ _longDateFormat : {
+ LT : "h:mm A",
+ L : "MM/DD/YYYY",
+ LL : "MMMM D YYYY",
+ LLL : "MMMM D YYYY LT",
+ LLLL : "dddd, MMMM D YYYY LT"
+ },
+ longDateFormat : function (key) {
+ var output = this._longDateFormat[key];
+ if (!output && this._longDateFormat[key.toUpperCase()]) {
+ output = this._longDateFormat[key.toUpperCase()].replace(/MMMM|MM|DD|dddd/g, function (val) {
+ return val.slice(1);
+ });
+ this._longDateFormat[key] = output;
+ }
+ return output;
+ },
+
+ isPM : function (input) {
+ // IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays
+ // Using charAt should be more compatible.
+ return ((input + '').toLowerCase().charAt(0) === 'p');
+ },
+
+ _meridiemParse : /[ap]\.?m?\.?/i,
+ meridiem : function (hours, minutes, isLower) {
+ if (hours > 11) {
+ return isLower ? 'pm' : 'PM';
+ } else {
+ return isLower ? 'am' : 'AM';
+ }
+ },
+
+ _calendar : {
+ sameDay : '[Today at] LT',
+ nextDay : '[Tomorrow at] LT',
+ nextWeek : 'dddd [at] LT',
+ lastDay : '[Yesterday at] LT',
+ lastWeek : '[Last] dddd [at] LT',
+ sameElse : 'L'
+ },
+ calendar : function (key, mom) {
+ var output = this._calendar[key];
+ return typeof output === 'function' ? output.apply(mom) : output;
+ },
+
+ _relativeTime : {
+ future : "in %s",
+ past : "%s ago",
+ s : "a few seconds",
+ m : "a minute",
+ mm : "%d minutes",
+ h : "an hour",
+ hh : "%d hours",
+ d : "a day",
+ dd : "%d days",
+ M : "a month",
+ MM : "%d months",
+ y : "a year",
+ yy : "%d years"
+ },
+ relativeTime : function (number, withoutSuffix, string, isFuture) {
+ var output = this._relativeTime[string];
+ return (typeof output === 'function') ?
+ output(number, withoutSuffix, string, isFuture) :
+ output.replace(/%d/i, number);
+ },
+ pastFuture : function (diff, output) {
+ var format = this._relativeTime[diff > 0 ? 'future' : 'past'];
+ return typeof format === 'function' ? format(output) : format.replace(/%s/i, output);
+ },
+
+ ordinal : function (number) {
+ return this._ordinal.replace("%d", number);
+ },
+ _ordinal : "%d",
+
+ preparse : function (string) {
+ return string;
+ },
+
+ postformat : function (string) {
+ return string;
+ },
+
+ week : function (mom) {
+ return weekOfYear(mom, this._week.dow, this._week.doy).week;
+ },
+
+ _week : {
+ dow : 0, // Sunday is the first day of the week.
+ doy : 6 // The week that contains Jan 1st is the first week of the year.
+ },
+
+ _invalidDate: 'Invalid date',
+ invalidDate: function () {
+ return this._invalidDate;
+ }
+ });
+
+ // Loads a language definition into the `languages` cache. The function
+ // takes a key and optionally values. If not in the browser and no values
+ // are provided, it will load the language file module. As a convenience,
+ // this function also returns the language values.
+ function loadLang(key, values) {
+ values.abbr = key;
+ if (!languages[key]) {
+ languages[key] = new Language();
+ }
+ languages[key].set(values);
+ return languages[key];
+ }
+
+ // Remove a language from the `languages` cache. Mostly useful in tests.
+ function unloadLang(key) {
+ delete languages[key];
+ }
+
+ // Determines which language definition to use and returns it.
+ //
+ // With no parameters, it will return the global language. If you
+ // pass in a language key, such as 'en', it will return the
+ // definition for 'en', so long as 'en' has already been loaded using
+ // moment.lang.
+ function getLangDefinition(key) {
+ var i = 0, j, lang, next, split,
+ get = function (k) {
+ if (!languages[k] && hasModule) {
+ try {
+ require('./lang/' + k);
+ } catch (e) { }
+ }
+ return languages[k];
+ };
+
+ if (!key) {
+ return moment.fn._lang;
+ }
+
+ if (!isArray(key)) {
+ //short-circuit everything else
+ lang = get(key);
+ if (lang) {
+ return lang;
+ }
+ key = [key];
+ }
+
+ //pick the language from the array
+ //try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each
+ //substring from most specific to least, but move to the next array item if it's a more specific variant than the current root
+ while (i < key.length) {
+ split = normalizeLanguage(key[i]).split('-');
+ j = split.length;
+ next = normalizeLanguage(key[i + 1]);
+ next = next ? next.split('-') : null;
+ while (j > 0) {
+ lang = get(split.slice(0, j).join('-'));
+ if (lang) {
+ return lang;
+ }
+ if (next && next.length >= j && compareArrays(split, next, true) >= j - 1) {
+ //the next array item is better than a shallower substring of this one
+ break;
+ }
+ j--;
+ }
+ i++;
+ }
+ return moment.fn._lang;
+ }
+
+ /************************************
+ Formatting
+ ************************************/
+
+
+ function removeFormattingTokens(input) {
+ if (input.match(/\[[\s\S]/)) {
+ return input.replace(/^\[|\]$/g, "");
+ }
+ return input.replace(/\\/g, "");
+ }
+
+ function makeFormatFunction(format) {
+ var array = format.match(formattingTokens), i, length;
+
+ for (i = 0, length = array.length; i < length; i++) {
+ if (formatTokenFunctions[array[i]]) {
+ array[i] = formatTokenFunctions[array[i]];
+ } else {
+ array[i] = removeFormattingTokens(array[i]);
+ }
+ }
+
+ return function (mom) {
+ var output = "";
+ for (i = 0; i < length; i++) {
+ output += array[i] instanceof Function ? array[i].call(mom, format) : array[i];
+ }
+ return output;
+ };
+ }
+
+ // format date using native date object
+ function formatMoment(m, format) {
+
+ if (!m.isValid()) {
+ return m.lang().invalidDate();
+ }
+
+ format = expandFormat(format, m.lang());
+
+ if (!formatFunctions[format]) {
+ formatFunctions[format] = makeFormatFunction(format);
+ }
+
+ return formatFunctions[format](m);
+ }
+
+ function expandFormat(format, lang) {
+ var i = 5;
+
+ function replaceLongDateFormatTokens(input) {
+ return lang.longDateFormat(input) || input;
+ }
+
+ localFormattingTokens.lastIndex = 0;
+ while (i >= 0 && localFormattingTokens.test(format)) {
+ format = format.replace(localFormattingTokens, replaceLongDateFormatTokens);
+ localFormattingTokens.lastIndex = 0;
+ i -= 1;
+ }
+
+ return format;
+ }
+
+
+ /************************************
+ Parsing
+ ************************************/
+
+
+ // get the regex to find the next token
+ function getParseRegexForToken(token, config) {
+ var a, strict = config._strict;
+ switch (token) {
+ case 'DDDD':
+ return parseTokenThreeDigits;
+ case 'YYYY':
+ case 'GGGG':
+ case 'gggg':
+ return strict ? parseTokenFourDigits : parseTokenOneToFourDigits;
+ case 'Y':
+ case 'G':
+ case 'g':
+ return parseTokenSignedNumber;
+ case 'YYYYYY':
+ case 'YYYYY':
+ case 'GGGGG':
+ case 'ggggg':
+ return strict ? parseTokenSixDigits : parseTokenOneToSixDigits;
+ case 'S':
+ if (strict) { return parseTokenOneDigit; }
+ /* falls through */
+ case 'SS':
+ if (strict) { return parseTokenTwoDigits; }
+ /* falls through */
+ case 'SSS':
+ if (strict) { return parseTokenThreeDigits; }
+ /* falls through */
+ case 'DDD':
+ return parseTokenOneToThreeDigits;
+ case 'MMM':
+ case 'MMMM':
+ case 'dd':
+ case 'ddd':
+ case 'dddd':
+ return parseTokenWord;
+ case 'a':
+ case 'A':
+ return getLangDefinition(config._l)._meridiemParse;
+ case 'X':
+ return parseTokenTimestampMs;
+ case 'Z':
+ case 'ZZ':
+ return parseTokenTimezone;
+ case 'T':
+ return parseTokenT;
+ case 'SSSS':
+ return parseTokenDigits;
+ case 'MM':
+ case 'DD':
+ case 'YY':
+ case 'GG':
+ case 'gg':
+ case 'HH':
+ case 'hh':
+ case 'mm':
+ case 'ss':
+ case 'ww':
+ case 'WW':
+ return strict ? parseTokenTwoDigits : parseTokenOneOrTwoDigits;
+ case 'M':
+ case 'D':
+ case 'd':
+ case 'H':
+ case 'h':
+ case 'm':
+ case 's':
+ case 'w':
+ case 'W':
+ case 'e':
+ case 'E':
+ return parseTokenOneOrTwoDigits;
+ default :
+ a = new RegExp(regexpEscape(unescapeFormat(token.replace('\\', '')), "i"));
+ return a;
+ }
+ }
+
+ function timezoneMinutesFromString(string) {
+ string = string || "";
+ var possibleTzMatches = (string.match(parseTokenTimezone) || []),
+ tzChunk = possibleTzMatches[possibleTzMatches.length - 1] || [],
+ parts = (tzChunk + '').match(parseTimezoneChunker) || ['-', 0, 0],
+ minutes = +(parts[1] * 60) + toInt(parts[2]);
+
+ return parts[0] === '+' ? -minutes : minutes;
+ }
+
+ // function to convert string input to date
+ function addTimeToArrayFromToken(token, input, config) {
+ var a, datePartArray = config._a;
+
+ switch (token) {
+ // MONTH
+ case 'M' : // fall through to MM
+ case 'MM' :
+ if (input != null) {
+ datePartArray[MONTH] = toInt(input) - 1;
+ }
+ break;
+ case 'MMM' : // fall through to MMMM
+ case 'MMMM' :
+ a = getLangDefinition(config._l).monthsParse(input);
+ // if we didn't find a month name, mark the date as invalid.
+ if (a != null) {
+ datePartArray[MONTH] = a;
+ } else {
+ config._pf.invalidMonth = input;
+ }
+ break;
+ // DAY OF MONTH
+ case 'D' : // fall through to DD
+ case 'DD' :
+ if (input != null) {
+ datePartArray[DATE] = toInt(input);
+ }
+ break;
+ // DAY OF YEAR
+ case 'DDD' : // fall through to DDDD
+ case 'DDDD' :
+ if (input != null) {
+ config._dayOfYear = toInt(input);
+ }
+
+ break;
+ // YEAR
+ case 'YY' :
+ datePartArray[YEAR] = toInt(input) + (toInt(input) > 68 ? 1900 : 2000);
+ break;
+ case 'YYYY' :
+ case 'YYYYY' :
+ case 'YYYYYY' :
+ datePartArray[YEAR] = toInt(input);
+ break;
+ // AM / PM
+ case 'a' : // fall through to A
+ case 'A' :
+ config._isPm = getLangDefinition(config._l).isPM(input);
+ break;
+ // 24 HOUR
+ case 'H' : // fall through to hh
+ case 'HH' : // fall through to hh
+ case 'h' : // fall through to hh
+ case 'hh' :
+ datePartArray[HOUR] = toInt(input);
+ break;
+ // MINUTE
+ case 'm' : // fall through to mm
+ case 'mm' :
+ datePartArray[MINUTE] = toInt(input);
+ break;
+ // SECOND
+ case 's' : // fall through to ss
+ case 'ss' :
+ datePartArray[SECOND] = toInt(input);
+ break;
+ // MILLISECOND
+ case 'S' :
+ case 'SS' :
+ case 'SSS' :
+ case 'SSSS' :
+ datePartArray[MILLISECOND] = toInt(('0.' + input) * 1000);
+ break;
+ // UNIX TIMESTAMP WITH MS
+ case 'X':
+ config._d = new Date(parseFloat(input) * 1000);
+ break;
+ // TIMEZONE
+ case 'Z' : // fall through to ZZ
+ case 'ZZ' :
+ config._useUTC = true;
+ config._tzm = timezoneMinutesFromString(input);
+ break;
+ case 'w':
+ case 'ww':
+ case 'W':
+ case 'WW':
+ case 'd':
+ case 'dd':
+ case 'ddd':
+ case 'dddd':
+ case 'e':
+ case 'E':
+ token = token.substr(0, 1);
+ /* falls through */
+ case 'gg':
+ case 'gggg':
+ case 'GG':
+ case 'GGGG':
+ case 'GGGGG':
+ token = token.substr(0, 2);
+ if (input) {
+ config._w = config._w || {};
+ config._w[token] = input;
+ }
+ break;
+ }
+ }
+
+ // convert an array to a date.
+ // the array should mirror the parameters below
+ // note: all values past the year are optional and will default to the lowest possible value.
+ // [year, month, day , hour, minute, second, millisecond]
+ function dateFromConfig(config) {
+ var i, date, input = [], currentDate,
+ yearToUse, fixYear, w, temp, lang, weekday, week;
+
+ if (config._d) {
+ return;
+ }
+
+ currentDate = currentDateArray(config);
+
+ //compute day of the year from weeks and weekdays
+ if (config._w && config._a[DATE] == null && config._a[MONTH] == null) {
+ fixYear = function (val) {
+ var int_val = parseInt(val, 10);
+ return val ?
+ (val.length < 3 ? (int_val > 68 ? 1900 + int_val : 2000 + int_val) : int_val) :
+ (config._a[YEAR] == null ? moment().weekYear() : config._a[YEAR]);
+ };
+
+ w = config._w;
+ if (w.GG != null || w.W != null || w.E != null) {
+ temp = dayOfYearFromWeeks(fixYear(w.GG), w.W || 1, w.E, 4, 1);
+ }
+ else {
+ lang = getLangDefinition(config._l);
+ weekday = w.d != null ? parseWeekday(w.d, lang) :
+ (w.e != null ? parseInt(w.e, 10) + lang._week.dow : 0);
+
+ week = parseInt(w.w, 10) || 1;
+
+ //if we're parsing 'd', then the low day numbers may be next week
+ if (w.d != null && weekday < lang._week.dow) {
+ week++;
+ }
+
+ temp = dayOfYearFromWeeks(fixYear(w.gg), week, weekday, lang._week.doy, lang._week.dow);
+ }
+
+ config._a[YEAR] = temp.year;
+ config._dayOfYear = temp.dayOfYear;
+ }
+
+ //if the day of the year is set, figure out what it is
+ if (config._dayOfYear) {
+ yearToUse = config._a[YEAR] == null ? currentDate[YEAR] : config._a[YEAR];
+
+ if (config._dayOfYear > daysInYear(yearToUse)) {
+ config._pf._overflowDayOfYear = true;
+ }
+
+ date = makeUTCDate(yearToUse, 0, config._dayOfYear);
+ config._a[MONTH] = date.getUTCMonth();
+ config._a[DATE] = date.getUTCDate();
+ }
+
+ // Default to current date.
+ // * if no year, month, day of month are given, default to today
+ // * if day of month is given, default month and year
+ // * if month is given, default only year
+ // * if year is given, don't default anything
+ for (i = 0; i < 3 && config._a[i] == null; ++i) {
+ config._a[i] = input[i] = currentDate[i];
+ }
+
+ // Zero out whatever was not defaulted, including time
+ for (; i < 7; i++) {
+ config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i];
+ }
+
+ // add the offsets to the time to be parsed so that we can have a clean array for checking isValid
+ input[HOUR] += toInt((config._tzm || 0) / 60);
+ input[MINUTE] += toInt((config._tzm || 0) % 60);
+
+ config._d = (config._useUTC ? makeUTCDate : makeDate).apply(null, input);
+ }
+
+ function dateFromObject(config) {
+ var normalizedInput;
+
+ if (config._d) {
+ return;
+ }
+
+ normalizedInput = normalizeObjectUnits(config._i);
+ config._a = [
+ normalizedInput.year,
+ normalizedInput.month,
+ normalizedInput.day,
+ normalizedInput.hour,
+ normalizedInput.minute,
+ normalizedInput.second,
+ normalizedInput.millisecond
+ ];
+
+ dateFromConfig(config);
+ }
+
+ function currentDateArray(config) {
+ var now = new Date();
+ if (config._useUTC) {
+ return [
+ now.getUTCFullYear(),
+ now.getUTCMonth(),
+ now.getUTCDate()
+ ];
+ } else {
+ return [now.getFullYear(), now.getMonth(), now.getDate()];
+ }
+ }
+
+ // date from string and format string
+ function makeDateFromStringAndFormat(config) {
+
+ config._a = [];
+ config._pf.empty = true;
+
+ // This array is used to make a Date, either with `new Date` or `Date.UTC`
+ var lang = getLangDefinition(config._l),
+ string = '' + config._i,
+ i, parsedInput, tokens, token, skipped,
+ stringLength = string.length,
+ totalParsedInputLength = 0;
+
+ tokens = expandFormat(config._f, lang).match(formattingTokens) || [];
+
+ for (i = 0; i < tokens.length; i++) {
+ token = tokens[i];
+ parsedInput = (string.match(getParseRegexForToken(token, config)) || [])[0];
+ if (parsedInput) {
+ skipped = string.substr(0, string.indexOf(parsedInput));
+ if (skipped.length > 0) {
+ config._pf.unusedInput.push(skipped);
+ }
+ string = string.slice(string.indexOf(parsedInput) + parsedInput.length);
+ totalParsedInputLength += parsedInput.length;
+ }
+ // don't parse if it's not a known token
+ if (formatTokenFunctions[token]) {
+ if (parsedInput) {
+ config._pf.empty = false;
+ }
+ else {
+ config._pf.unusedTokens.push(token);
+ }
+ addTimeToArrayFromToken(token, parsedInput, config);
+ }
+ else if (config._strict && !parsedInput) {
+ config._pf.unusedTokens.push(token);
+ }
+ }
+
+ // add remaining unparsed input length to the string
+ config._pf.charsLeftOver = stringLength - totalParsedInputLength;
+ if (string.length > 0) {
+ config._pf.unusedInput.push(string);
+ }
+
+ // handle am pm
+ if (config._isPm && config._a[HOUR] < 12) {
+ config._a[HOUR] += 12;
+ }
+ // if is 12 am, change hours to 0
+ if (config._isPm === false && config._a[HOUR] === 12) {
+ config._a[HOUR] = 0;
+ }
+
+ dateFromConfig(config);
+ checkOverflow(config);
+ }
+
+ function unescapeFormat(s) {
+ return s.replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function (matched, p1, p2, p3, p4) {
+ return p1 || p2 || p3 || p4;
+ });
+ }
+
+ // Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript
+ function regexpEscape(s) {
+ return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
+ }
+
+ // date from string and array of format strings
+ function makeDateFromStringAndArray(config) {
+ var tempConfig,
+ bestMoment,
+
+ scoreToBeat,
+ i,
+ currentScore;
+
+ if (config._f.length === 0) {
+ config._pf.invalidFormat = true;
+ config._d = new Date(NaN);
+ return;
+ }
+
+ for (i = 0; i < config._f.length; i++) {
+ currentScore = 0;
+ tempConfig = extend({}, config);
+ tempConfig._pf = defaultParsingFlags();
+ tempConfig._f = config._f[i];
+ makeDateFromStringAndFormat(tempConfig);
+
+ if (!isValid(tempConfig)) {
+ continue;
+ }
+
+ // if there is any input that was not parsed add a penalty for that format
+ currentScore += tempConfig._pf.charsLeftOver;
+
+ //or tokens
+ currentScore += tempConfig._pf.unusedTokens.length * 10;
+
+ tempConfig._pf.score = currentScore;
+
+ if (scoreToBeat == null || currentScore < scoreToBeat) {
+ scoreToBeat = currentScore;
+ bestMoment = tempConfig;
+ }
+ }
+
+ extend(config, bestMoment || tempConfig);
+ }
+
+ // date from iso format
+ function makeDateFromString(config) {
+ var i, l,
+ string = config._i,
+ match = isoRegex.exec(string);
+
+ if (match) {
+ config._pf.iso = true;
+ for (i = 0, l = isoDates.length; i < l; i++) {
+ if (isoDates[i][1].exec(string)) {
+ // match[5] should be "T" or undefined
+ config._f = isoDates[i][0] + (match[6] || " ");
+ break;
+ }
+ }
+ for (i = 0, l = isoTimes.length; i < l; i++) {
+ if (isoTimes[i][1].exec(string)) {
+ config._f += isoTimes[i][0];
+ break;
+ }
+ }
+ if (string.match(parseTokenTimezone)) {
+ config._f += "Z";
+ }
+ makeDateFromStringAndFormat(config);
+ }
+ else {
+ config._d = new Date(string);
+ }
+ }
+
+ function makeDateFromInput(config) {
+ var input = config._i,
+ matched = aspNetJsonRegex.exec(input);
+
+ if (input === undefined) {
+ config._d = new Date();
+ } else if (matched) {
+ config._d = new Date(+matched[1]);
+ } else if (typeof input === 'string') {
+ makeDateFromString(config);
+ } else if (isArray(input)) {
+ config._a = input.slice(0);
+ dateFromConfig(config);
+ } else if (isDate(input)) {
+ config._d = new Date(+input);
+ } else if (typeof(input) === 'object') {
+ dateFromObject(config);
+ } else {
+ config._d = new Date(input);
+ }
+ }
+
+ function makeDate(y, m, d, h, M, s, ms) {
+ //can't just apply() to create a date:
+ //http://stackoverflow.com/questions/181348/instantiating-a-javascript-object-by-calling-prototype-constructor-apply
+ var date = new Date(y, m, d, h, M, s, ms);
+
+ //the date constructor doesn't accept years < 1970
+ if (y < 1970) {
+ date.setFullYear(y);
+ }
+ return date;
+ }
+
+ function makeUTCDate(y) {
+ var date = new Date(Date.UTC.apply(null, arguments));
+ if (y < 1970) {
+ date.setUTCFullYear(y);
+ }
+ return date;
+ }
+
+ function parseWeekday(input, language) {
+ if (typeof input === 'string') {
+ if (!isNaN(input)) {
+ input = parseInt(input, 10);
+ }
+ else {
+ input = language.weekdaysParse(input);
+ if (typeof input !== 'number') {
+ return null;
+ }
+ }
+ }
+ return input;
+ }
+
+ /************************************
+ Relative Time
+ ************************************/
+
+
+ // helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize
+ function substituteTimeAgo(string, number, withoutSuffix, isFuture, lang) {
+ return lang.relativeTime(number || 1, !!withoutSuffix, string, isFuture);
+ }
+
+ function relativeTime(milliseconds, withoutSuffix, lang) {
+ var seconds = round(Math.abs(milliseconds) / 1000),
+ minutes = round(seconds / 60),
+ hours = round(minutes / 60),
+ days = round(hours / 24),
+ years = round(days / 365),
+ args = seconds < 45 && ['s', seconds] ||
+ minutes === 1 && ['m'] ||
+ minutes < 45 && ['mm', minutes] ||
+ hours === 1 && ['h'] ||
+ hours < 22 && ['hh', hours] ||
+ days === 1 && ['d'] ||
+ days <= 25 && ['dd', days] ||
+ days <= 45 && ['M'] ||
+ days < 345 && ['MM', round(days / 30)] ||
+ years === 1 && ['y'] || ['yy', years];
+ args[2] = withoutSuffix;
+ args[3] = milliseconds > 0;
+ args[4] = lang;
+ return substituteTimeAgo.apply({}, args);
+ }
+
+
+ /************************************
+ Week of Year
+ ************************************/
+
+
+ // firstDayOfWeek 0 = sun, 6 = sat
+ // the day of the week that starts the week
+ // (usually sunday or monday)
+ // firstDayOfWeekOfYear 0 = sun, 6 = sat
+ // the first week is the week that contains the first
+ // of this day of the week
+ // (eg. ISO weeks use thursday (4))
+ function weekOfYear(mom, firstDayOfWeek, firstDayOfWeekOfYear) {
+ var end = firstDayOfWeekOfYear - firstDayOfWeek,
+ daysToDayOfWeek = firstDayOfWeekOfYear - mom.day(),
+ adjustedMoment;
+
+
+ if (daysToDayOfWeek > end) {
+ daysToDayOfWeek -= 7;
+ }
+
+ if (daysToDayOfWeek < end - 7) {
+ daysToDayOfWeek += 7;
+ }
+
+ adjustedMoment = moment(mom).add('d', daysToDayOfWeek);
+ return {
+ week: Math.ceil(adjustedMoment.dayOfYear() / 7),
+ year: adjustedMoment.year()
+ };
+ }
+
+ //http://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday
+ function dayOfYearFromWeeks(year, week, weekday, firstDayOfWeekOfYear, firstDayOfWeek) {
+ var d = makeUTCDate(year, 0, 1).getUTCDay(), daysToAdd, dayOfYear;
+
+ weekday = weekday != null ? weekday : firstDayOfWeek;
+ daysToAdd = firstDayOfWeek - d + (d > firstDayOfWeekOfYear ? 7 : 0) - (d < firstDayOfWeek ? 7 : 0);
+ dayOfYear = 7 * (week - 1) + (weekday - firstDayOfWeek) + daysToAdd + 1;
+
+ return {
+ year: dayOfYear > 0 ? year : year - 1,
+ dayOfYear: dayOfYear > 0 ? dayOfYear : daysInYear(year - 1) + dayOfYear
+ };
+ }
+
+ /************************************
+ Top Level Functions
+ ************************************/
+
+ function makeMoment(config) {
+ var input = config._i,
+ format = config._f;
+
+ if (input === null) {
+ return moment.invalid({nullInput: true});
+ }
+
+ if (typeof input === 'string') {
+ config._i = input = getLangDefinition().preparse(input);
+ }
+
+ if (moment.isMoment(input)) {
+ config = cloneMoment(input);
+
+ config._d = new Date(+input._d);
+ } else if (format) {
+ if (isArray(format)) {
+ makeDateFromStringAndArray(config);
+ } else {
+ makeDateFromStringAndFormat(config);
+ }
+ } else {
+ makeDateFromInput(config);
+ }
+
+ return new Moment(config);
+ }
+
+ moment = function (input, format, lang, strict) {
+ var c;
+
+ if (typeof(lang) === "boolean") {
+ strict = lang;
+ lang = undefined;
+ }
+ // object construction must be done this way.
+ // https://github.com/moment/moment/issues/1423
+ c = {};
+ c._isAMomentObject = true;
+ c._i = input;
+ c._f = format;
+ c._l = lang;
+ c._strict = strict;
+ c._isUTC = false;
+ c._pf = defaultParsingFlags();
+
+ return makeMoment(c);
+ };
+
+ // creating with utc
+ moment.utc = function (input, format, lang, strict) {
+ var c;
+
+ if (typeof(lang) === "boolean") {
+ strict = lang;
+ lang = undefined;
+ }
+ // object construction must be done this way.
+ // https://github.com/moment/moment/issues/1423
+ c = {};
+ c._isAMomentObject = true;
+ c._useUTC = true;
+ c._isUTC = true;
+ c._l = lang;
+ c._i = input;
+ c._f = format;
+ c._strict = strict;
+ c._pf = defaultParsingFlags();
+
+ return makeMoment(c).utc();
+ };
+
+ // creating with unix timestamp (in seconds)
+ moment.unix = function (input) {
+ return moment(input * 1000);
+ };
+
+ // duration
+ moment.duration = function (input, key) {
+ var duration = input,
+ // matching against regexp is expensive, do it on demand
+ match = null,
+ sign,
+ ret,
+ parseIso;
+
+ if (moment.isDuration(input)) {
+ duration = {
+ ms: input._milliseconds,
+ d: input._days,
+ M: input._months
+ };
+ } else if (typeof input === 'number') {
+ duration = {};
+ if (key) {
+ duration[key] = input;
+ } else {
+ duration.milliseconds = input;
+ }
+ } else if (!!(match = aspNetTimeSpanJsonRegex.exec(input))) {
+ sign = (match[1] === "-") ? -1 : 1;
+ duration = {
+ y: 0,
+ d: toInt(match[DATE]) * sign,
+ h: toInt(match[HOUR]) * sign,
+ m: toInt(match[MINUTE]) * sign,
+ s: toInt(match[SECOND]) * sign,
+ ms: toInt(match[MILLISECOND]) * sign
+ };
+ } else if (!!(match = isoDurationRegex.exec(input))) {
+ sign = (match[1] === "-") ? -1 : 1;
+ parseIso = function (inp) {
+ // We'd normally use ~~inp for this, but unfortunately it also
+ // converts floats to ints.
+ // inp may be undefined, so careful calling replace on it.
+ var res = inp && parseFloat(inp.replace(',', '.'));
+ // apply sign while we're at it
+ return (isNaN(res) ? 0 : res) * sign;
+ };
+ duration = {
+ y: parseIso(match[2]),
+ M: parseIso(match[3]),
+ d: parseIso(match[4]),
+ h: parseIso(match[5]),
+ m: parseIso(match[6]),
+ s: parseIso(match[7]),
+ w: parseIso(match[8])
+ };
+ }
+
+ ret = new Duration(duration);
+
+ if (moment.isDuration(input) && input.hasOwnProperty('_lang')) {
+ ret._lang = input._lang;
+ }
+
+ return ret;
+ };
+
+ // version number
+ moment.version = VERSION;
+
+ // default format
+ moment.defaultFormat = isoFormat;
+
+ // This function will be called whenever a moment is mutated.
+ // It is intended to keep the offset in sync with the timezone.
+ moment.updateOffset = function () {};
+
+ // This function will load languages and then set the global language. If
+ // no arguments are passed in, it will simply return the current global
+ // language key.
+ moment.lang = function (key, values) {
+ var r;
+ if (!key) {
+ return moment.fn._lang._abbr;
+ }
+ if (values) {
+ loadLang(normalizeLanguage(key), values);
+ } else if (values === null) {
+ unloadLang(key);
+ key = 'en';
+ } else if (!languages[key]) {
+ getLangDefinition(key);
+ }
+ r = moment.duration.fn._lang = moment.fn._lang = getLangDefinition(key);
+ return r._abbr;
+ };
+
+ // returns language data
+ moment.langData = function (key) {
+ if (key && key._lang && key._lang._abbr) {
+ key = key._lang._abbr;
+ }
+ return getLangDefinition(key);
+ };
+
+ // compare moment object
+ moment.isMoment = function (obj) {
+ return obj instanceof Moment ||
+ (obj != null && obj.hasOwnProperty('_isAMomentObject'));
+ };
+
+ // for typechecking Duration objects
+ moment.isDuration = function (obj) {
+ return obj instanceof Duration;
+ };
+
+ for (i = lists.length - 1; i >= 0; --i) {
+ makeList(lists[i]);
+ }
+
+ moment.normalizeUnits = function (units) {
+ return normalizeUnits(units);
+ };
+
+ moment.invalid = function (flags) {
+ var m = moment.utc(NaN);
+ if (flags != null) {
+ extend(m._pf, flags);
+ }
+ else {
+ m._pf.userInvalidated = true;
+ }
+
+ return m;
+ };
+
+ moment.parseZone = function (input) {
+ return moment(input).parseZone();
+ };
+
+ /************************************
+ Moment Prototype
+ ************************************/
+
+
+ extend(moment.fn = Moment.prototype, {
+
+ clone : function () {
+ return moment(this);
+ },
+
+ valueOf : function () {
+ return +this._d + ((this._offset || 0) * 60000);
+ },
+
+ unix : function () {
+ return Math.floor(+this / 1000);
+ },
+
+ toString : function () {
+ return this.clone().lang('en').format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ");
+ },
+
+ toDate : function () {
+ return this._offset ? new Date(+this) : this._d;
+ },
+
+ toISOString : function () {
+ var m = moment(this).utc();
+ if (0 < m.year() && m.year() <= 9999) {
+ return formatMoment(m, 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]');
+ } else {
+ return formatMoment(m, 'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]');
+ }
+ },
+
+ toArray : function () {
+ var m = this;
+ return [
+ m.year(),
+ m.month(),
+ m.date(),
+ m.hours(),
+ m.minutes(),
+ m.seconds(),
+ m.milliseconds()
+ ];
+ },
+
+ isValid : function () {
+ return isValid(this);
+ },
+
+ isDSTShifted : function () {
+
+ if (this._a) {
+ return this.isValid() && compareArrays(this._a, (this._isUTC ? moment.utc(this._a) : moment(this._a)).toArray()) > 0;
+ }
+
+ return false;
+ },
+
+ parsingFlags : function () {
+ return extend({}, this._pf);
+ },
+
+ invalidAt: function () {
+ return this._pf.overflow;
+ },
+
+ utc : function () {
+ return this.zone(0);
+ },
+
+ local : function () {
+ this.zone(0);
+ this._isUTC = false;
+ return this;
+ },
+
+ format : function (inputString) {
+ var output = formatMoment(this, inputString || moment.defaultFormat);
+ return this.lang().postformat(output);
+ },
+
+ add : function (input, val) {
+ var dur;
+ // switch args to support add('s', 1) and add(1, 's')
+ if (typeof input === 'string') {
+ dur = moment.duration(+val, input);
+ } else {
+ dur = moment.duration(input, val);
+ }
+ addOrSubtractDurationFromMoment(this, dur, 1);
+ return this;
+ },
+
+ subtract : function (input, val) {
+ var dur;
+ // switch args to support subtract('s', 1) and subtract(1, 's')
+ if (typeof input === 'string') {
+ dur = moment.duration(+val, input);
+ } else {
+ dur = moment.duration(input, val);
+ }
+ addOrSubtractDurationFromMoment(this, dur, -1);
+ return this;
+ },
+
+ diff : function (input, units, asFloat) {
+ var that = makeAs(input, this),
+ zoneDiff = (this.zone() - that.zone()) * 6e4,
+ diff, output;
+
+ units = normalizeUnits(units);
+
+ if (units === 'year' || units === 'month') {
+ // average number of days in the months in the given dates
+ diff = (this.daysInMonth() + that.daysInMonth()) * 432e5; // 24 * 60 * 60 * 1000 / 2
+ // difference in months
+ output = ((this.year() - that.year()) * 12) + (this.month() - that.month());
+ // adjust by taking difference in days, average number of days
+ // and dst in the given months.
+ output += ((this - moment(this).startOf('month')) -
+ (that - moment(that).startOf('month'))) / diff;
+ // same as above but with zones, to negate all dst
+ output -= ((this.zone() - moment(this).startOf('month').zone()) -
+ (that.zone() - moment(that).startOf('month').zone())) * 6e4 / diff;
+ if (units === 'year') {
+ output = output / 12;
+ }
+ } else {
+ diff = (this - that);
+ output = units === 'second' ? diff / 1e3 : // 1000
+ units === 'minute' ? diff / 6e4 : // 1000 * 60
+ units === 'hour' ? diff / 36e5 : // 1000 * 60 * 60
+ units === 'day' ? (diff - zoneDiff) / 864e5 : // 1000 * 60 * 60 * 24, negate dst
+ units === 'week' ? (diff - zoneDiff) / 6048e5 : // 1000 * 60 * 60 * 24 * 7, negate dst
+ diff;
+ }
+ return asFloat ? output : absRound(output);
+ },
+
+ from : function (time, withoutSuffix) {
+ return moment.duration(this.diff(time)).lang(this.lang()._abbr).humanize(!withoutSuffix);
+ },
+
+ fromNow : function (withoutSuffix) {
+ return this.from(moment(), withoutSuffix);
+ },
+
+ calendar : function () {
+ // We want to compare the start of today, vs this.
+ // Getting start-of-today depends on whether we're zone'd or not.
+ var sod = makeAs(moment(), this).startOf('day'),
+ diff = this.diff(sod, 'days', true),
+ format = diff < -6 ? 'sameElse' :
+ diff < -1 ? 'lastWeek' :
+ diff < 0 ? 'lastDay' :
+ diff < 1 ? 'sameDay' :
+ diff < 2 ? 'nextDay' :
+ diff < 7 ? 'nextWeek' : 'sameElse';
+ return this.format(this.lang().calendar(format, this));
+ },
+
+ isLeapYear : function () {
+ return isLeapYear(this.year());
+ },
+
+ isDST : function () {
+ return (this.zone() < this.clone().month(0).zone() ||
+ this.zone() < this.clone().month(5).zone());
+ },
+
+ day : function (input) {
+ var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay();
+ if (input != null) {
+ input = parseWeekday(input, this.lang());
+ return this.add({ d : input - day });
+ } else {
+ return day;
+ }
+ },
+
+ month : function (input) {
+ var utc = this._isUTC ? 'UTC' : '',
+ dayOfMonth;
+
+ if (input != null) {
+ if (typeof input === 'string') {
+ input = this.lang().monthsParse(input);
+ if (typeof input !== 'number') {
+ return this;
+ }
+ }
+
+ dayOfMonth = this.date();
+ this.date(1);
+ this._d['set' + utc + 'Month'](input);
+ this.date(Math.min(dayOfMonth, this.daysInMonth()));
+
+ moment.updateOffset(this);
+ return this;
+ } else {
+ return this._d['get' + utc + 'Month']();
+ }
+ },
+
+ startOf: function (units) {
+ units = normalizeUnits(units);
+ // the following switch intentionally omits break keywords
+ // to utilize falling through the cases.
+ switch (units) {
+ case 'year':
+ this.month(0);
+ /* falls through */
+ case 'month':
+ this.date(1);
+ /* falls through */
+ case 'week':
+ case 'isoWeek':
+ case 'day':
+ this.hours(0);
+ /* falls through */
+ case 'hour':
+ this.minutes(0);
+ /* falls through */
+ case 'minute':
+ this.seconds(0);
+ /* falls through */
+ case 'second':
+ this.milliseconds(0);
+ /* falls through */
+ }
+
+ // weeks are a special case
+ if (units === 'week') {
+ this.weekday(0);
+ } else if (units === 'isoWeek') {
+ this.isoWeekday(1);
+ }
+
+ return this;
+ },
+
+ endOf: function (units) {
+ units = normalizeUnits(units);
+ return this.startOf(units).add((units === 'isoWeek' ? 'week' : units), 1).subtract('ms', 1);
+ },
+
+ isAfter: function (input, units) {
+ units = typeof units !== 'undefined' ? units : 'millisecond';
+ return +this.clone().startOf(units) > +moment(input).startOf(units);
+ },
+
+ isBefore: function (input, units) {
+ units = typeof units !== 'undefined' ? units : 'millisecond';
+ return +this.clone().startOf(units) < +moment(input).startOf(units);
+ },
+
+ isSame: function (input, units) {
+ units = units || 'ms';
+ return +this.clone().startOf(units) === +makeAs(input, this).startOf(units);
+ },
+
+ min: function (other) {
+ other = moment.apply(null, arguments);
+ return other < this ? this : other;
+ },
+
+ max: function (other) {
+ other = moment.apply(null, arguments);
+ return other > this ? this : other;
+ },
+
+ zone : function (input) {
+ var offset = this._offset || 0;
+ if (input != null) {
+ if (typeof input === "string") {
+ input = timezoneMinutesFromString(input);
+ }
+ if (Math.abs(input) < 16) {
+ input = input * 60;
+ }
+ this._offset = input;
+ this._isUTC = true;
+ if (offset !== input) {
+ addOrSubtractDurationFromMoment(this, moment.duration(offset - input, 'm'), 1, true);
+ }
+ } else {
+ return this._isUTC ? offset : this._d.getTimezoneOffset();
+ }
+ return this;
+ },
+
+ zoneAbbr : function () {
+ return this._isUTC ? "UTC" : "";
+ },
+
+ zoneName : function () {
+ return this._isUTC ? "Coordinated Universal Time" : "";
+ },
+
+ parseZone : function () {
+ if (this._tzm) {
+ this.zone(this._tzm);
+ } else if (typeof this._i === 'string') {
+ this.zone(this._i);
+ }
+ return this;
+ },
+
+ hasAlignedHourOffset : function (input) {
+ if (!input) {
+ input = 0;
+ }
+ else {
+ input = moment(input).zone();
+ }
+
+ return (this.zone() - input) % 60 === 0;
+ },
+
+ daysInMonth : function () {
+ return daysInMonth(this.year(), this.month());
+ },
+
+ dayOfYear : function (input) {
+ var dayOfYear = round((moment(this).startOf('day') - moment(this).startOf('year')) / 864e5) + 1;
+ return input == null ? dayOfYear : this.add("d", (input - dayOfYear));
+ },
+
+ quarter : function () {
+ return Math.ceil((this.month() + 1.0) / 3.0);
+ },
+
+ weekYear : function (input) {
+ var year = weekOfYear(this, this.lang()._week.dow, this.lang()._week.doy).year;
+ return input == null ? year : this.add("y", (input - year));
+ },
+
+ isoWeekYear : function (input) {
+ var year = weekOfYear(this, 1, 4).year;
+ return input == null ? year : this.add("y", (input - year));
+ },
+
+ week : function (input) {
+ var week = this.lang().week(this);
+ return input == null ? week : this.add("d", (input - week) * 7);
+ },
+
+ isoWeek : function (input) {
+ var week = weekOfYear(this, 1, 4).week;
+ return input == null ? week : this.add("d", (input - week) * 7);
+ },
+
+ weekday : function (input) {
+ var weekday = (this.day() + 7 - this.lang()._week.dow) % 7;
+ return input == null ? weekday : this.add("d", input - weekday);
+ },
+
+ isoWeekday : function (input) {
+ // behaves the same as moment#day except
+ // as a getter, returns 7 instead of 0 (1-7 range instead of 0-6)
+ // as a setter, sunday should belong to the previous week.
+ return input == null ? this.day() || 7 : this.day(this.day() % 7 ? input : input - 7);
+ },
+
+ get : function (units) {
+ units = normalizeUnits(units);
+ return this[units]();
+ },
+
+ set : function (units, value) {
+ units = normalizeUnits(units);
+ if (typeof this[units] === 'function') {
+ this[units](value);
+ }
+ return this;
+ },
+
+ // If passed a language key, it will set the language for this
+ // instance. Otherwise, it will return the language configuration
+ // variables for this instance.
+ lang : function (key) {
+ if (key === undefined) {
+ return this._lang;
+ } else {
+ this._lang = getLangDefinition(key);
+ return this;
+ }
+ }
+ });
+
+ // helper for adding shortcuts
+ function makeGetterAndSetter(name, key) {
+ moment.fn[name] = moment.fn[name + 's'] = function (input) {
+ var utc = this._isUTC ? 'UTC' : '';
+ if (input != null) {
+ this._d['set' + utc + key](input);
+ moment.updateOffset(this);
+ return this;
+ } else {
+ return this._d['get' + utc + key]();
+ }
+ };
+ }
+
+ // loop through and add shortcuts (Month, Date, Hours, Minutes, Seconds, Milliseconds)
+ for (i = 0; i < proxyGettersAndSetters.length; i ++) {
+ makeGetterAndSetter(proxyGettersAndSetters[i].toLowerCase().replace(/s$/, ''), proxyGettersAndSetters[i]);
+ }
+
+ // add shortcut for year (uses different syntax than the getter/setter 'year' == 'FullYear')
+ makeGetterAndSetter('year', 'FullYear');
+
+ // add plural methods
+ moment.fn.days = moment.fn.day;
+ moment.fn.months = moment.fn.month;
+ moment.fn.weeks = moment.fn.week;
+ moment.fn.isoWeeks = moment.fn.isoWeek;
+
+ // add aliased format methods
+ moment.fn.toJSON = moment.fn.toISOString;
+
+ /************************************
+ Duration Prototype
+ ************************************/
+
+
+ extend(moment.duration.fn = Duration.prototype, {
+
+ _bubble : function () {
+ var milliseconds = this._milliseconds,
+ days = this._days,
+ months = this._months,
+ data = this._data,
+ seconds, minutes, hours, years;
+
+ // The following code bubbles up values, see the tests for
+ // examples of what that means.
+ data.milliseconds = milliseconds % 1000;
+
+ seconds = absRound(milliseconds / 1000);
+ data.seconds = seconds % 60;
+
+ minutes = absRound(seconds / 60);
+ data.minutes = minutes % 60;
+
+ hours = absRound(minutes / 60);
+ data.hours = hours % 24;
+
+ days += absRound(hours / 24);
+ data.days = days % 30;
+
+ months += absRound(days / 30);
+ data.months = months % 12;
+
+ years = absRound(months / 12);
+ data.years = years;
+ },
+
+ weeks : function () {
+ return absRound(this.days() / 7);
+ },
+
+ valueOf : function () {
+ return this._milliseconds +
+ this._days * 864e5 +
+ (this._months % 12) * 2592e6 +
+ toInt(this._months / 12) * 31536e6;
+ },
+
+ humanize : function (withSuffix) {
+ var difference = +this,
+ output = relativeTime(difference, !withSuffix, this.lang());
+
+ if (withSuffix) {
+ output = this.lang().pastFuture(difference, output);
+ }
+
+ return this.lang().postformat(output);
+ },
+
+ add : function (input, val) {
+ // supports only 2.0-style add(1, 's') or add(moment)
+ var dur = moment.duration(input, val);
+
+ this._milliseconds += dur._milliseconds;
+ this._days += dur._days;
+ this._months += dur._months;
+
+ this._bubble();
+
+ return this;
+ },
+
+ subtract : function (input, val) {
+ var dur = moment.duration(input, val);
+
+ this._milliseconds -= dur._milliseconds;
+ this._days -= dur._days;
+ this._months -= dur._months;
+
+ this._bubble();
+
+ return this;
+ },
+
+ get : function (units) {
+ units = normalizeUnits(units);
+ return this[units.toLowerCase() + 's']();
+ },
+
+ as : function (units) {
+ units = normalizeUnits(units);
+ return this['as' + units.charAt(0).toUpperCase() + units.slice(1) + 's']();
+ },
+
+ lang : moment.fn.lang,
+
+ toIsoString : function () {
+ // inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js
+ var years = Math.abs(this.years()),
+ months = Math.abs(this.months()),
+ days = Math.abs(this.days()),
+ hours = Math.abs(this.hours()),
+ minutes = Math.abs(this.minutes()),
+ seconds = Math.abs(this.seconds() + this.milliseconds() / 1000);
+
+ if (!this.asSeconds()) {
+ // this is the same as C#'s (Noda) and python (isodate)...
+ // but not other JS (goog.date)
+ return 'P0D';
+ }
+
+ return (this.asSeconds() < 0 ? '-' : '') +
+ 'P' +
+ (years ? years + 'Y' : '') +
+ (months ? months + 'M' : '') +
+ (days ? days + 'D' : '') +
+ ((hours || minutes || seconds) ? 'T' : '') +
+ (hours ? hours + 'H' : '') +
+ (minutes ? minutes + 'M' : '') +
+ (seconds ? seconds + 'S' : '');
+ }
+ });
+
+ function makeDurationGetter(name) {
+ moment.duration.fn[name] = function () {
+ return this._data[name];
+ };
+ }
+
+ function makeDurationAsGetter(name, factor) {
+ moment.duration.fn['as' + name] = function () {
+ return +this / factor;
+ };
+ }
+
+ for (i in unitMillisecondFactors) {
+ if (unitMillisecondFactors.hasOwnProperty(i)) {
+ makeDurationAsGetter(i, unitMillisecondFactors[i]);
+ makeDurationGetter(i.toLowerCase());
+ }
+ }
+
+ makeDurationAsGetter('Weeks', 6048e5);
+ moment.duration.fn.asMonths = function () {
+ return (+this - this.years() * 31536e6) / 2592e6 + this.years() * 12;
+ };
+
+
+ /************************************
+ Default Lang
+ ************************************/
+
+
+ // Set default language, other languages will inherit from English.
+ moment.lang('en', {
+ ordinal : function (number) {
+ var b = number % 10,
+ output = (toInt(number % 100 / 10) === 1) ? 'th' :
+ (b === 1) ? 'st' :
+ (b === 2) ? 'nd' :
+ (b === 3) ? 'rd' : 'th';
+ return number + output;
+ }
+ });
+
+ /* EMBED_LANGUAGES */
+
+ /************************************
+ Exposing Moment
+ ************************************/
+
+ function makeGlobal(deprecate) {
+ var warned = false, local_moment = moment;
+ /*global ender:false */
+ if (typeof ender !== 'undefined') {
+ return;
+ }
+ // here, `this` means `window` in the browser, or `global` on the server
+ // add `moment` as a global object via a string identifier,
+ // for Closure Compiler "advanced" mode
+ if (deprecate) {
+ global.moment = function () {
+ if (!warned && console && console.warn) {
+ warned = true;
+ console.warn(
+ "Accessing Moment through the global scope is " +
+ "deprecated, and will be removed in an upcoming " +
+ "release.");
+ }
+ return local_moment.apply(null, arguments);
+ };
+ extend(global.moment, local_moment);
+ } else {
+ global['moment'] = moment;
+ }
+ }
+
+ // CommonJS module is defined
+ if (hasModule) {
+ module.exports = moment;
+ makeGlobal(true);
+ } else if (typeof define === "function" && define.amd) {
+ define("moment", function (require, exports, module) {
+ if (module.config && module.config() && module.config().noGlobal !== true) {
+ // If user provided noGlobal, he is aware of global
+ makeGlobal(module.config().noGlobal === undefined);
+ }
+
+ return moment;
+ });
+ } else {
+ makeGlobal();
+ }
+}).call(this);
+
+
+/*
+Version 3.0.0
+=========================================================
+bootstrap-datetimepicker.js
+https://github.com/Eonasdan/bootstrap-datetimepicker
+=========================================================
+The MIT License (MIT)
+
+Copyright (c) 2014 Jonathan Peterson
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+; (function (factory) {
+ if (typeof define === 'function' && define.amd) {
+ // AMD is used - Register as an anonymous module.
+ define(['jquery', 'moment'], factory);
+ } else {
+ // AMD is not used - Attempt to fetch dependencies from scope.
+ if (!jQuery) {
+ throw 'bootstrap-datetimepicker requires jQuery to be loaded first';
+ } else if (!moment) {
+ throw 'bootstrap-datetimepicker requires moment.js to be loaded first';
+ } else {
+ factory(jQuery, moment);
+ }
+ }
+}
+
+(function ($, moment) {
+ if (typeof moment === 'undefined') {
+ alert("momentjs is requried");
+ throw new Error('momentjs is required');
+ };
+
+ var dpgId = 0,
+
+ pMoment = moment,
+
+// ReSharper disable once InconsistentNaming
+ DateTimePicker = function (element, options) {
+ var defaults = $.fn.datetimepicker.defaults,
+
+ icons = {
+ time: 'fa fa-clock-o',
+ date: 'fa fa-calendar',
+ up: 'fa fa-chevron-up',
+ down: 'fa fa-chevron-down'
+ },
+
+ picker = this,
+
+ init = function () {
+
+ var icon = false, i, dDate, longDateFormat;
+ picker.options = $.extend({}, defaults, options);
+ picker.options.icons = $.extend({}, icons, picker.options.icons);
+
+ picker.element = $(element);
+
+ dataToOptions();
+
+ if (!(picker.options.pickTime || picker.options.pickDate))
+ throw new Error('Must choose at least one picker');
+
+ picker.id = dpgId++;
+ pMoment.lang(picker.options.language);
+ picker.date = pMoment();
+ picker.unset = false;
+ picker.isInput = picker.element.is('input');
+ picker.component = false;
+
+ if (picker.element.hasClass('input-group')) {
+ if (picker.element.find('.datepickerbutton').size() == 0) {//in case there is more then one 'input-group-addon' Issue #48
+ picker.component = picker.element.find("[class^='input-group-']");
+ }
+ else {
+ picker.component = picker.element.find('.datepickerbutton');
+ }
+ }
+ picker.format = picker.options.format;
+
+ longDateFormat = pMoment()._lang._longDateFormat;
+
+ if (!picker.format) {
+ picker.format = (picker.options.pickDate ? longDateFormat.L : '');
+ if (picker.options.pickDate && picker.options.pickTime) picker.format += ' ';
+ picker.format += (picker.options.pickTime ? longDateFormat.LT : '');
+ if (picker.options.useSeconds) {
+ if (~longDateFormat.LT.indexOf(' A')) {
+ picker.format = picker.format.split(" A")[0] + ":ss A";
+ }
+ else {
+ picker.format += ':ss';
+ }
+ }
+ }
+ picker.use24hours = picker.format.toLowerCase().indexOf("a") < 1;
+
+ if (picker.component) icon = picker.component.find('span');
+
+ if (picker.options.pickTime) {
+ if (icon) icon.addClass(picker.options.icons.time);
+ }
+ if (picker.options.pickDate) {
+ if (icon) {
+ icon.removeClass(picker.options.icons.time);
+ icon.addClass(picker.options.icons.date);
+ }
+ }
+
+ picker.widget = $(getTemplate()).appendTo('body');
+
+ if (picker.options.useSeconds && !picker.use24hours) {
+ picker.widget.width(300);
+ }
+
+ picker.minViewMode = picker.options.minViewMode || 0;
+ if (typeof picker.minViewMode === 'string') {
+ switch (picker.minViewMode) {
+ case 'months':
+ picker.minViewMode = 1;
+ break;
+ case 'years':
+ picker.minViewMode = 2;
+ break;
+ default:
+ picker.minViewMode = 0;
+ break;
+ }
+ }
+ picker.viewMode = picker.options.viewMode || 0;
+ if (typeof picker.viewMode === 'string') {
+ switch (picker.viewMode) {
+ case 'months':
+ picker.viewMode = 1;
+ break;
+ case 'years':
+ picker.viewMode = 2;
+ break;
+ default:
+ picker.viewMode = 0;
+ break;
+ }
+ }
+
+ picker.options.disabledDates = indexGivenDates(picker.options.disabledDates);
+ picker.options.enabledDates = indexGivenDates(picker.options.enabledDates);
+
+ picker.startViewMode = picker.viewMode;
+ picker.setMinDate(picker.options.minDate);
+ picker.setMaxDate(picker.options.maxDate);
+ fillDow();
+ fillMonths();
+ fillHours();
+ fillMinutes();
+ fillSeconds();
+ update();
+ showMode();
+ attachDatePickerEvents();
+ if (picker.options.defaultDate !== "" && getPickerInput().val() == "") picker.setValue(picker.options.defaultDate);
+ if (picker.options.minuteStepping !== 1) {
+ var rInterval = picker.options.minuteStepping;
+ picker.date.minutes((Math.round(picker.date.minutes() / rInterval) * rInterval) % 60).seconds(0);
+ }
+ },
+
+ getPickerInput = function () {
+ if (picker.isInput) {
+ return picker.element;
+ } else {
+ return dateStr = picker.element.find('input');
+ }
+ },
+
+ dataToOptions = function () {
+ var eData
+ if (picker.element.is('input')) {
+ eData = picker.element.data();
+ }
+ else {
+ eData = picker.element.data();
+ }
+ if (eData.dateFormat !== undefined) picker.options.format = eData.dateFormat;
+ if (eData.datePickdate !== undefined) picker.options.pickDate = eData.datePickdate;
+ if (eData.datePicktime !== undefined) picker.options.pickTime = eData.datePicktime;
+ if (eData.dateUseminutes !== undefined) picker.options.useMinutes = eData.dateUseminutes;
+ if (eData.dateUseseconds !== undefined) picker.options.useSeconds = eData.dateUseseconds;
+ if (eData.dateUsecurrent !== undefined) picker.options.useCurrent = eData.dateUsecurrent;
+ if (eData.dateMinutestepping !== undefined) picker.options.minuteStepping = eData.dateMinutestepping;
+ if (eData.dateMindate !== undefined) picker.options.minDate = eData.dateMindate;
+ if (eData.dateMaxdate !== undefined) picker.options.maxDate = eData.dateMaxdate;
+ if (eData.dateShowtoday !== undefined) picker.options.showToday = eData.dateShowtoday;
+ if (eData.dateCollapse !== undefined) picker.options.collapse = eData.dateCollapse;
+ if (eData.dateLanguage !== undefined) picker.options.language = eData.dateLanguage;
+ if (eData.dateDefaultdate !== undefined) picker.options.defaultDate = eData.dateDefaultdate;
+ if (eData.dateDisableddates !== undefined) picker.options.disabledDates = eData.dateDisableddates;
+ if (eData.dateEnableddates !== undefined) picker.options.enabledDates = eData.dateEnableddates;
+ if (eData.dateIcons !== undefined) picker.options.icons = eData.dateIcons;
+ if (eData.dateUsestrict !== undefined) picker.options.useStrict = eData.dateUsestrict;
+ if (eData.dateDirection !== undefined) picker.options.direction = eData.dateDirection;
+ if (eData.dateSidebyside !== undefined) picker.options.sideBySide = eData.dateSidebyside;
+ },
+
+ place = function () {
+ var position = 'absolute',
+ offset = picker.component ? picker.component.offset() : picker.element.offset(), $window = $(window);
+ picker.width = picker.component ? picker.component.outerWidth() : picker.element.outerWidth();
+ offset.top = offset.top + picker.element.outerHeight();
+
+ var placePosition;
+ if (picker.options.direction === 'up') {
+ placePosition = 'top'
+ } else if (picker.options.direction === 'bottom') {
+ placePosition = 'bottom'
+ } else if (picker.options.direction === 'auto') {
+ if (offset.top + picker.widget.height() > $window.height() + $window.scrollTop() && picker.widget.height() + picker.element.outerHeight() < offset.top) {
+ placePosition = 'top';
+ } else {
+ placePosition = 'bottom';
+ }
+ };
+ if (placePosition === 'top') {
+ offset.top -= picker.widget.height() + picker.element.outerHeight() + 15;
+ picker.widget.addClass('top').removeClass('bottom');
+ } else {
+ offset.top += 1;
+ picker.widget.addClass('bottom').removeClass('top');
+ }
+
+ if (picker.options.width !== undefined) {
+ picker.widget.width(picker.options.width);
+ }
+
+ if (picker.options.orientation === 'left') {
+ picker.widget.addClass('left-oriented');
+ offset.left = offset.left - picker.widget.width() + 20;
+ }
+
+ if (isInFixed()) {
+ position = 'fixed';
+ offset.top -= $window.scrollTop();
+ offset.left -= $window.scrollLeft();
+ }
+
+ if ($window.width() < offset.left + picker.widget.outerWidth()) {
+ offset.right = $window.width() - offset.left - picker.width;
+ offset.left = 'auto';
+ picker.widget.addClass('pull-right');
+ } else {
+ offset.right = 'auto';
+ picker.widget.removeClass('pull-right');
+ }
+
+ picker.widget.css({
+ position: position,
+ top: offset.top,
+ left: offset.left,
+ right: offset.right
+ });
+ },
+
+ notifyChange = function (oldDate, eventType) {
+ if (pMoment(picker.date).isSame(pMoment(oldDate))) return;
+ picker.element.trigger({
+ type: 'dp.change',
+ date: pMoment(picker.date),
+ oldDate: pMoment(oldDate)
+ });
+
+ if (eventType !== 'change')
+ picker.element.change();
+ },
+
+ notifyError = function (date) {
+ picker.element.trigger({
+ type: 'dp.error',
+ date: pMoment(date)
+ });
+ },
+
+ update = function (newDate) {
+ pMoment.lang(picker.options.language);
+ var dateStr = newDate;
+ if (!dateStr) {
+ dateStr = getPickerInput().val();
+ if (dateStr) picker.date = pMoment(dateStr, picker.format, picker.options.useStrict);
+ if (!picker.date) picker.date = pMoment();
+ }
+ picker.viewDate = pMoment(picker.date).startOf("month");
+ fillDate();
+ fillTime();
+ },
+
+ fillDow = function () {
+ pMoment.lang(picker.options.language);
+ var html = $(''), weekdaysMin = pMoment.weekdaysMin(), i;
+ if (pMoment()._lang._week.dow == 0) { // starts on Sunday
+ for (i = 0; i < 7; i++) {
+ html.append('' + weekdaysMin[i] + ' ');
+ }
+ } else {
+ for (i = 1; i < 8; i++) {
+ if (i == 7) {
+ html.append('' + weekdaysMin[0] + ' ');
+ } else {
+ html.append('' + weekdaysMin[i] + ' ');
+ }
+ }
+ }
+ picker.widget.find('.datepicker-days thead').append(html);
+ },
+
+ fillMonths = function () {
+ pMoment.lang(picker.options.language);
+ var html = '', i = 0, monthsShort = pMoment.monthsShort();
+ while (i < 12) {
+ html += '' + monthsShort[i++] + ' ';
+ }
+ picker.widget.find('.datepicker-months td').append(html);
+ },
+
+ fillDate = function () {
+ if(!picker.options.pickDate) return;
+ pMoment.lang(picker.options.language);
+ var year = picker.viewDate.year(),
+ month = picker.viewDate.month(),
+ startYear = picker.options.minDate.year(),
+ startMonth = picker.options.minDate.month(),
+ endYear = picker.options.maxDate.year(),
+ endMonth = picker.options.maxDate.month(),
+ currentDate,
+ prevMonth, nextMonth, html = [], row, clsName, i, days, yearCont, currentYear, months = pMoment.months();
+
+ picker.widget.find('.datepicker-days').find('.disabled').removeClass('disabled');
+ picker.widget.find('.datepicker-months').find('.disabled').removeClass('disabled');
+ picker.widget.find('.datepicker-years').find('.disabled').removeClass('disabled');
+
+ picker.widget.find('.datepicker-days th:eq(1)').text(
+ months[month] + ' ' + year);
+
+ prevMonth = pMoment(picker.viewDate).subtract("months", 1);
+ days = prevMonth.daysInMonth();
+ prevMonth.date(days).startOf('week');
+ if ((year == startYear && month <= startMonth) || year < startYear) {
+ picker.widget.find('.datepicker-days th:eq(0)').addClass('disabled');
+ }
+ if ((year == endYear && month >= endMonth) || year > endYear) {
+ picker.widget.find('.datepicker-days th:eq(2)').addClass('disabled');
+ }
+
+ nextMonth = pMoment(prevMonth).add(42, "d");
+ while (prevMonth.isBefore(nextMonth)) {
+ if (prevMonth.weekday() === pMoment().startOf('week').weekday()) {
+ row = $(' ');
+ html.push(row);
+ }
+ clsName = '';
+ if (prevMonth.year() < year || (prevMonth.year() == year && prevMonth.month() < month)) {
+ clsName += ' old';
+ } else if (prevMonth.year() > year || (prevMonth.year() == year && prevMonth.month() > month)) {
+ clsName += ' new';
+ }
+ if (prevMonth.isSame(pMoment({ y: picker.date.year(), M: picker.date.month(), d: picker.date.date() }))) {
+ clsName += ' active';
+ }
+ if (isInDisableDates(prevMonth) || !isInEnableDates(prevMonth)) {
+ clsName += ' disabled';
+ }
+ if (picker.options.showToday === true) {
+ if (prevMonth.isSame(pMoment(), 'day')) {
+ clsName += ' today';
+ }
+ }
+ if (picker.options.daysOfWeekDisabled) {
+ for (i in picker.options.daysOfWeekDisabled) {
+ if (prevMonth.day() == picker.options.daysOfWeekDisabled[i]) {
+ clsName += ' disabled';
+ break;
+ }
+ }
+ }
+ row.append('' + prevMonth.date() + ' ');
+
+ currentDate = prevMonth.date();
+ prevMonth.add(1, "d");
+
+ if (currentDate == prevMonth.date()) {
+ prevMonth.add(1, "d");
+ }
+ }
+ picker.widget.find('.datepicker-days tbody').empty().append(html);
+ currentYear = picker.date.year(), months = picker.widget.find('.datepicker-months')
+ .find('th:eq(1)').text(year).end().find('span').removeClass('active');
+ if (currentYear === year) {
+ months.eq(picker.date.month()).addClass('active');
+ }
+ if (currentYear - 1 < startYear) {
+ picker.widget.find('.datepicker-months th:eq(0)').addClass('disabled');
+ }
+ if (currentYear + 1 > endYear) {
+ picker.widget.find('.datepicker-months th:eq(2)').addClass('disabled');
+ }
+ for (i = 0; i < 12; i++) {
+ if ((year == startYear && startMonth > i) || (year < startYear)) {
+ $(months[i]).addClass('disabled');
+ } else if ((year == endYear && endMonth < i) || (year > endYear)) {
+ $(months[i]).addClass('disabled');
+ }
+ }
+
+ html = '';
+ year = parseInt(year / 10, 10) * 10;
+ yearCont = picker.widget.find('.datepicker-years').find(
+ 'th:eq(1)').text(year + '-' + (year + 9)).end().find('td');
+ picker.widget.find('.datepicker-years').find('th').removeClass('disabled');
+ if (startYear > year) {
+ picker.widget.find('.datepicker-years').find('th:eq(0)').addClass('disabled');
+ }
+ if (endYear < year + 9) {
+ picker.widget.find('.datepicker-years').find('th:eq(2)').addClass('disabled');
+ }
+ year -= 1;
+ for (i = -1; i < 11; i++) {
+ html += '' + year + ' ';
+ year += 1;
+ }
+ yearCont.html(html);
+ },
+
+ fillHours = function () {
+ pMoment.lang(picker.options.language);
+ var table = picker.widget.find('.timepicker .timepicker-hours table'), html = '', current, i, j;
+ table.parent().hide();
+ if (picker.use24hours) {
+ current = 0;
+ for (i = 0; i < 6; i += 1) {
+ html += ' ';
+ for (j = 0; j < 4; j += 1) {
+ html += '' + padLeft(current.toString()) + ' ';
+ current++;
+ }
+ html += ' ';
+ }
+ }
+ else {
+ current = 1;
+ for (i = 0; i < 3; i += 1) {
+ html += '';
+ for (j = 0; j < 4; j += 1) {
+ html += '' + padLeft(current.toString()) + ' ';
+ current++;
+ }
+ html += ' ';
+ }
+ }
+ table.html(html);
+ },
+
+ fillMinutes = function () {
+ var table = picker.widget.find('.timepicker .timepicker-minutes table'), html = '', current = 0, i, j, step = picker.options.minuteStepping;
+ table.parent().hide();
+ if (step == 1) step = 5;
+ for (i = 0; i < Math.ceil(60 / step / 4) ; i++) {
+ html += '';
+ for (j = 0; j < 4; j += 1) {
+ if (current < 60) {
+ html += '' + padLeft(current.toString()) + ' ';
+ current += step;
+ } else {
+ html += ' ';
+ }
+ }
+ html += ' ';
+ }
+ table.html(html);
+ },
+
+ fillSeconds = function () {
+ var table = picker.widget.find('.timepicker .timepicker-seconds table'), html = '', current = 0, i, j;
+ table.parent().hide();
+ for (i = 0; i < 3; i++) {
+ html += '';
+ for (j = 0; j < 4; j += 1) {
+ html += '' + padLeft(current.toString()) + ' ';
+ current += 5;
+ }
+ html += ' ';
+ }
+ table.html(html);
+ },
+
+ fillTime = function () {
+ if (!picker.date) return;
+ var timeComponents = picker.widget.find('.timepicker span[data-time-component]'),
+ hour = picker.date.hours(),
+ period = 'AM';
+ if (!picker.use24hours) {
+ if (hour >= 12) period = 'PM';
+ if (hour === 0) hour = 12;
+ else if (hour != 12) hour = hour % 12;
+ picker.widget.find('.timepicker [data-action=togglePeriod]').text(period);
+ }
+ timeComponents.filter('[data-time-component=hours]').text(padLeft(hour));
+ timeComponents.filter('[data-time-component=minutes]').text(padLeft(picker.date.minutes()));
+ timeComponents.filter('[data-time-component=seconds]').text(padLeft(picker.date.second()));
+ },
+
+ click = function (e) {
+ e.stopPropagation();
+ e.preventDefault();
+ picker.unset = false;
+ var target = $(e.target).closest('span, td, th'), month, year, step, day, oldDate = pMoment(picker.date);
+ if (target.length === 1) {
+ if (!target.is('.disabled')) {
+ switch (target[0].nodeName.toLowerCase()) {
+ case 'th':
+ switch (target[0].className) {
+ case 'switch':
+ showMode(1);
+ break;
+ case 'prev':
+ case 'next':
+ step = dpGlobal.modes[picker.viewMode].navStep;
+ if (target[0].className === 'prev') step = step * -1;
+ picker.viewDate.add(step, dpGlobal.modes[picker.viewMode].navFnc);
+ fillDate();
+ break;
+ }
+ break;
+ case 'span':
+ if (target.is('.month')) {
+ month = target.parent().find('span').index(target);
+ picker.viewDate.month(month);
+ } else {
+ year = parseInt(target.text(), 10) || 0;
+ picker.viewDate.year(year);
+ }
+ if (picker.viewMode === picker.minViewMode) {
+ picker.date = pMoment({
+ y: picker.viewDate.year(),
+ M: picker.viewDate.month(),
+ d: picker.viewDate.date(),
+ h: picker.date.hours(),
+ m: picker.date.minutes(),
+ s: picker.date.seconds()
+ });
+ notifyChange(oldDate, e.type);
+ set();
+ }
+ showMode(-1);
+ fillDate();
+ break;
+ case 'td':
+ if (target.is('.day')) {
+ day = parseInt(target.text(), 10) || 1;
+ month = picker.viewDate.month();
+ year = picker.viewDate.year();
+ if (target.is('.old')) {
+ if (month === 0) {
+ month = 11;
+ year -= 1;
+ } else {
+ month -= 1;
+ }
+ } else if (target.is('.new')) {
+ if (month == 11) {
+ month = 0;
+ year += 1;
+ } else {
+ month += 1;
+ }
+ }
+ picker.date = pMoment({
+ y: year,
+ M: month,
+ d: day,
+ h: picker.date.hours(),
+ m: picker.date.minutes(),
+ s: picker.date.seconds()
+ }
+ );
+ picker.viewDate = pMoment({
+ y: year, M: month, d: Math.min(28, day)
+ });
+ fillDate();
+ set();
+ notifyChange(oldDate, e.type);
+ }
+ break;
+ }
+ }
+ }
+ },
+
+ actions = {
+ incrementHours: function () {
+ checkDate("add", "hours", 1);
+ },
+
+ incrementMinutes: function () {
+ checkDate("add", "minutes", picker.options.minuteStepping);
+ },
+
+ incrementSeconds: function () {
+ checkDate("add", "seconds", 1);
+ },
+
+ decrementHours: function () {
+ checkDate("subtract", "hours", 1);
+ },
+
+ decrementMinutes: function () {
+ checkDate("subtract", "minutes", picker.options.minuteStepping);
+ },
+
+ decrementSeconds: function () {
+ checkDate("subtract", "seconds", 1);
+ },
+
+ togglePeriod: function () {
+ var hour = picker.date.hours();
+ if (hour >= 12) hour -= 12;
+ else hour += 12;
+ picker.date.hours(hour);
+ },
+
+ showPicker: function () {
+ picker.widget.find('.timepicker > div:not(.timepicker-picker)').hide();
+ picker.widget.find('.timepicker .timepicker-picker').show();
+ },
+
+ showHours: function () {
+ picker.widget.find('.timepicker .timepicker-picker').hide();
+ picker.widget.find('.timepicker .timepicker-hours').show();
+ },
+
+ showMinutes: function () {
+ picker.widget.find('.timepicker .timepicker-picker').hide();
+ picker.widget.find('.timepicker .timepicker-minutes').show();
+ },
+
+ showSeconds: function () {
+ picker.widget.find('.timepicker .timepicker-picker').hide();
+ picker.widget.find('.timepicker .timepicker-seconds').show();
+ },
+
+ selectHour: function (e) {
+ var period = picker.widget.find('.timepicker [data-action=togglePeriod]').text(), hour = parseInt($(e.target).text(), 10);
+ if (period == "PM") hour += 12
+ picker.date.hours(hour);
+ actions.showPicker.call(picker);
+ },
+
+ selectMinute: function (e) {
+ picker.date.minutes(parseInt($(e.target).text(), 10));
+ actions.showPicker.call(picker);
+ },
+
+ selectSecond: function (e) {
+ picker.date.seconds(parseInt($(e.target).text(), 10));
+ actions.showPicker.call(picker);
+ }
+ },
+
+ doAction = function (e) {
+ var oldDate = pMoment(picker.date), action = $(e.currentTarget).data('action'), rv = actions[action].apply(picker, arguments);
+ stopEvent(e);
+ if (!picker.date) picker.date = pMoment({ y: 1970 });
+ set();
+ fillTime();
+ notifyChange(oldDate, e.type);
+ return rv;
+ },
+
+ stopEvent = function (e) {
+ e.stopPropagation();
+ e.preventDefault();
+ },
+
+ change = function (e) {
+ pMoment.lang(picker.options.language);
+ var input = $(e.target), oldDate = pMoment(picker.date), newDate = pMoment(input.val(), picker.format, picker.options.useStrict);
+ if (newDate.isValid() && !isInDisableDates(newDate) && isInEnableDates(newDate)) {
+ update();
+ picker.setValue(newDate);
+ notifyChange(oldDate, e.type);
+ set();
+ }
+ else {
+ picker.viewDate = oldDate;
+ notifyChange(oldDate, e.type);
+ notifyError(newDate);
+ picker.unset = true;
+ }
+ },
+
+ showMode = function (dir) {
+ if (dir) {
+ picker.viewMode = Math.max(picker.minViewMode, Math.min(2, picker.viewMode + dir));
+ }
+ var f = dpGlobal.modes[picker.viewMode].clsName;
+ picker.widget.find('.datepicker > div').hide().filter('.datepicker-' + dpGlobal.modes[picker.viewMode].clsName).show();
+ },
+
+ attachDatePickerEvents = function () {
+ var $this, $parent, expanded, closed, collapseData;
+ picker.widget.on('click', '.datepicker *', $.proxy(click, this)); // this handles date picker clicks
+ picker.widget.on('click', '[data-action]', $.proxy(doAction, this)); // this handles time picker clicks
+ picker.widget.on('mousedown', $.proxy(stopEvent, this));
+ if (picker.options.pickDate && picker.options.pickTime) {
+ picker.widget.on('click.togglePicker', '.accordion-toggle', function (e) {
+ e.stopPropagation();
+ $this = $(this);
+ $parent = $this.closest('ul');
+ expanded = $parent.find('.in');
+ closed = $parent.find('.collapse:not(.in)');
+
+ if (expanded && expanded.length) {
+ collapseData = expanded.data('collapse');
+ if (collapseData && collapseData.date - transitioning) return;
+ expanded.collapse('hide');
+ closed.collapse('show');
+ $this.find('span').toggleClass(picker.options.icons.time + ' ' + picker.options.icons.date);
+ picker.element.find('.input-group-addon span').toggleClass(picker.options.icons.time + ' ' + picker.options.icons.date);
+ }
+ });
+ }
+ if (picker.isInput) {
+ picker.element.on({
+ 'focus': $.proxy(picker.show, this),
+ 'change': $.proxy(change, this),
+ 'blur': $.proxy(picker.hide, this)
+ });
+ } else {
+ picker.element.on({
+ 'change': $.proxy(change, this)
+ }, 'input');
+ if (picker.component) {
+ picker.component.on('click', $.proxy(picker.show, this));
+ } else {
+ picker.element.on('click', $.proxy(picker.show, this));
+ }
+ }
+ },
+
+ attachDatePickerGlobalEvents = function () {
+ $(window).on(
+ 'resize.datetimepicker' + picker.id, $.proxy(place, this));
+ if (!picker.isInput) {
+ $(document).on(
+ 'mousedown.datetimepicker' + picker.id, $.proxy(picker.hide, this));
+ }
+ },
+
+ detachDatePickerEvents = function () {
+ picker.widget.off('click', '.datepicker *', picker.click);
+ picker.widget.off('click', '[data-action]');
+ picker.widget.off('mousedown', picker.stopEvent);
+ if (picker.options.pickDate && picker.options.pickTime) {
+ picker.widget.off('click.togglePicker');
+ }
+ if (picker.isInput) {
+ picker.element.off({
+ 'focus': picker.show,
+ 'change': picker.change
+ });
+ } else {
+ picker.element.off({
+ 'change': picker.change
+ }, 'input');
+ if (picker.component) {
+ picker.component.off('click', picker.show);
+ } else {
+ picker.element.off('click', picker.show);
+ }
+ }
+ },
+
+ detachDatePickerGlobalEvents = function () {
+ $(window).off('resize.datetimepicker' + picker.id);
+ if (!picker.isInput) {
+ $(document).off('mousedown.datetimepicker' + picker.id);
+ }
+ },
+
+ isInFixed = function () {
+ if (picker.element) {
+ var parents = picker.element.parents(), inFixed = false, i;
+ for (i = 0; i < parents.length; i++) {
+ if ($(parents[i]).css('position') == 'fixed') {
+ inFixed = true;
+ break;
+ }
+ }
+ ;
+ return inFixed;
+ } else {
+ return false;
+ }
+ },
+
+ set = function () {
+ pMoment.lang(picker.options.language);
+ var formatted = '', input;
+ if (!picker.unset) formatted = pMoment(picker.date).format(picker.format);
+ getPickerInput().val(formatted);
+ picker.element.data('date', formatted);
+ if (!picker.options.pickTime) picker.hide();
+ },
+
+ checkDate = function (direction, unit, amount) {
+ pMoment.lang(picker.options.language);
+ var newDate;
+ if (direction == "add") {
+ newDate = pMoment(picker.date);
+ if (newDate.hours() == 23) newDate.add(amount, unit);
+ newDate.add(amount, unit);
+ }
+ else {
+ newDate = pMoment(picker.date).subtract(amount, unit);
+ }
+ if (isInDisableDates(pMoment(newDate.subtract(amount, unit))) || isInDisableDates(newDate)) {
+ notifyError(newDate.format(picker.format));
+ return;
+ }
+
+ if (direction == "add") {
+ picker.date.add(amount, unit);
+ }
+ else {
+ picker.date.subtract(amount, unit);
+ }
+ picker.unset = false;
+ },
+
+ isInDisableDates = function (date) {
+ pMoment.lang(picker.options.language);
+ if (date.isAfter(picker.options.maxDate) || date.isBefore(picker.options.minDate)) return true;
+ if (picker.options.disabledDates === false) {
+ return false;
+ }
+ return picker.options.disabledDates[pMoment(date).format("YYYY-MM-DD")] === true;
+ },
+ isInEnableDates = function (date) {
+ pMoment.lang(picker.options.language);
+ if (picker.options.enabledDates === false) {
+ return true;
+ }
+ return picker.options.enabledDates[pMoment(date).format("YYYY-MM-DD")] === true;
+ },
+
+ indexGivenDates = function (givenDatesArray) {
+ // Store given enabledDates and disabledDates as keys.
+ // This way we can check their existence in O(1) time instead of looping through whole array.
+ // (for example: picker.options.enabledDates['2014-02-27'] === true)
+ var givenDatesIndexed = {};
+ var givenDatesCount = 0;
+ for (i = 0; i < givenDatesArray.length; i++) {
+ dDate = pMoment(givenDatesArray[i]);
+ if (dDate.isValid()) {
+ givenDatesIndexed[dDate.format("YYYY-MM-DD")] = true;
+ givenDatesCount++;
+ }
+ }
+ if (givenDatesCount > 0) {
+ return givenDatesIndexed;
+ }
+ return false;
+ },
+
+ padLeft = function (string) {
+ string = string.toString();
+ if (string.length >= 2) return string;
+ else return '0' + string;
+ },
+
+ getTemplate = function () {
+ if (picker.options.pickDate && picker.options.pickTime) {
+ var ret = '';
+ ret = '';
+ return ret;
+ } else if (picker.options.pickTime) {
+ return (
+ ''
+ );
+ } else {
+ return (
+ ''
+ );
+ }
+ },
+
+ dpGlobal = {
+ modes: [
+ {
+ clsName: 'days',
+ navFnc: 'month',
+ navStep: 1
+ },
+ {
+ clsName: 'months',
+ navFnc: 'year',
+ navStep: 1
+ },
+ {
+ clsName: 'years',
+ navFnc: 'year',
+ navStep: 10
+ }],
+ headTemplate:
+ '' +
+ '' +
+ '‹ › ' +
+ ' ' +
+ ' ',
+ contTemplate:
+ ' '
+ },
+
+ tpGlobal = {
+ hourTemplate: ' ',
+ minuteTemplate: ' ',
+ secondTemplate: ' '
+ };
+
+ dpGlobal.template =
+ '' +
+ '
' + dpGlobal.headTemplate + '
' +
+ '
' +
+ '' +
+ '
' + dpGlobal.headTemplate + dpGlobal.contTemplate + '
' +
+ '
' +
+ '' +
+ '
' + dpGlobal.headTemplate + dpGlobal.contTemplate + '
' +
+ '
';
+
+ tpGlobal.getTemplate = function () {
+ return (
+ '' +
+ '
' +
+ '' +
+ ' ' +
+ ' ' +
+ '' + (picker.options.useMinutes ? ' ' : '') + ' ' +
+ (picker.options.useSeconds ?
+ ' ' : '') +
+ (picker.use24hours ? '' : ' ') +
+ ' ' +
+ '' +
+ '' + tpGlobal.hourTemplate + ' ' +
+ ': ' +
+ '' + (picker.options.useMinutes ? tpGlobal.minuteTemplate : '00 ') + ' ' +
+ (picker.options.useSeconds ?
+ ': ' + tpGlobal.secondTemplate + ' ' : '') +
+ (picker.use24hours ? '' : ' ' +
+ ' ') +
+ ' ' +
+ '' +
+ ' ' +
+ ' ' +
+ '' + (picker.options.useMinutes ? ' ' : '') + ' ' +
+ (picker.options.useSeconds ?
+ ' ' : '') +
+ (picker.use24hours ? '' : ' ') +
+ ' ' +
+ '
' +
+ '
' +
+ '' +
+ '' +
+ (picker.options.useSeconds ?
+ '' : '')
+ );
+ };
+
+ picker.destroy = function () {
+ detachDatePickerEvents();
+ detachDatePickerGlobalEvents();
+ picker.widget.remove();
+ picker.element.removeData('DateTimePicker');
+ if (picker.component)
+ picker.component.removeData('DateTimePicker');
+ };
+
+ picker.show = function (e) {
+ if (picker.options.useCurrent) {
+ if (getPickerInput().val() == '') {
+ if (picker.options.minuteStepping !== 1) {
+ var mDate = pMoment(),
+ rInterval = picker.options.minuteStepping;
+ mDate.minutes((Math.round(mDate.minutes() / rInterval) * rInterval) % 60)
+ .seconds(0);
+ picker.setValue(mDate.format(picker.format))
+ } else {
+ picker.setValue(pMoment().format(picker.format))
+ }
+ };
+ }
+ if (picker.widget.hasClass("picker-open")) {
+ picker.widget.hide();
+ picker.widget.removeClass("picker-open");
+ }
+ else {
+ picker.widget.show();
+ picker.widget.addClass("picker-open");
+ }
+ picker.height = picker.component ? picker.component.outerHeight() : picker.element.outerHeight();
+ place();
+ picker.element.trigger({
+ type: 'dp.show',
+ date: pMoment(picker.date)
+ });
+ attachDatePickerGlobalEvents();
+ if (e) {
+ stopEvent(e);
+ }
+ },
+
+ picker.disable = function () {
+ var input = picker.element.find('input');
+ if (input.prop('disabled')) return;
+
+ input.prop('disabled', true);
+ detachDatePickerEvents();
+ },
+
+ picker.enable = function () {
+ var input = picker.element.find('input');
+ if (!input.prop('disabled')) return;
+
+ input.prop('disabled', false);
+ attachDatePickerEvents();
+ },
+
+ picker.hide = function (event) {
+ if (event && $(event.target).is(picker.element.attr("id")))
+ return;
+ // Ignore event if in the middle of a picker transition
+ var collapse = picker.widget.find('.collapse'), i, collapseData;
+ for (i = 0; i < collapse.length; i++) {
+ collapseData = collapse.eq(i).data('collapse');
+ if (collapseData && collapseData.date - transitioning)
+ return;
+ }
+ picker.widget.hide();
+ picker.widget.removeClass("picker-open");
+ picker.viewMode = picker.startViewMode;
+ showMode();
+ picker.element.trigger({
+ type: 'dp.hide',
+ date: pMoment(picker.date)
+ });
+ detachDatePickerGlobalEvents();
+ },
+
+ picker.setValue = function (newDate) {
+ pMoment.lang(picker.options.language);
+ if (!newDate) {
+ picker.unset = true;
+ set();
+ } else {
+ picker.unset = false;
+ }
+ if (!pMoment.isMoment(newDate)) newDate = pMoment(newDate, picker.format);
+ if (newDate.isValid()) {
+ picker.date = newDate;
+ set();
+ picker.viewDate = pMoment({ y: picker.date.year(), M: picker.date.month() });
+ fillDate();
+ fillTime();
+ }
+ else {
+ notifyError(newDate);
+ }
+ },
+
+ picker.getDate = function () {
+ if (picker.unset) return null;
+ return picker.date;
+ },
+
+ picker.setDate = function (date) {
+ var oldDate = pMoment(picker.date);
+ if (!date) {
+ picker.setValue(null);
+ } else {
+ picker.setValue(date);
+ }
+ notifyChange(oldDate, "function");
+ },
+
+ picker.setDisabledDates = function (dates) {
+ picker.options.disabledDates = indexGivenDates(dates);
+ if (picker.viewDate) update();
+ },
+ picker.setEnabledDates = function (dates) {
+ picker.options.enabledDates = indexGivenDates(dates);
+ if (picker.viewDate) update();
+ },
+
+ picker.setMaxDate = function (date) {
+ if (date == undefined) return;
+ picker.options.maxDate = pMoment(date);
+ if (picker.viewDate) update();
+ },
+
+ picker.setMinDate = function (date) {
+ if (date == undefined) return;
+ picker.options.minDate = pMoment(date);
+ if (picker.viewDate) update();
+ };
+
+ init();
+ };
+
+ $.fn.datetimepicker = function (options) {
+ return this.each(function () {
+ var $this = $(this), data = $this.data('DateTimePicker');
+ if (!data) $this.data('DateTimePicker', new DateTimePicker(this, options));
+ });
+ };
+
+ $.fn.datetimepicker.defaults = {
+ pickDate: true,
+ pickTime: true,
+ useMinutes: true,
+ useSeconds: false,
+ useCurrent: true,
+ minuteStepping: 1,
+ minDate: new pMoment({ y: 1900 }),
+ maxDate: new pMoment().add(100, "y"),
+ showToday: true,
+ collapse: true,
+ language: "en",
+ defaultDate: "",
+ disabledDates: false,
+ enabledDates: false,
+ icons: {},
+ useStrict: false,
+ direction: "auto",
+ sideBySide: false,
+ daysOfWeekDisabled: false
+ };
+
+}));
+
+
+
+
+
+// moment.js language configuration
+// language : french (fr)
+// author : John Fischer : https://github.com/jfroffice
+
+(function (factory) {
+ if (typeof define === 'function' && define.amd) {
+ define(['moment'], factory); // AMD
+ } else if (typeof exports === 'object') {
+ module.exports = factory(require('../moment')); // Node
+ } else {
+ factory(window.moment); // Browser global
+ }
+}(function (moment) {
+ return moment.lang('fr', {
+ months : "janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre".split("_"),
+ monthsShort : "janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.".split("_"),
+ weekdays : "dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi".split("_"),
+ weekdaysShort : "dim._lun._mar._mer._jeu._ven._sam.".split("_"),
+ weekdaysMin : "Di_Lu_Ma_Me_Je_Ve_Sa".split("_"),
+ longDateFormat : {
+ LT : "HH:mm",
+ L : "DD/MM/YYYY",
+ LL : "D MMMM YYYY",
+ LLL : "D MMMM YYYY LT",
+ LLLL : "dddd D MMMM YYYY LT"
+ },
+ calendar : {
+ sameDay: "[Aujourd'hui à] LT",
+ nextDay: '[Demain à] LT',
+ nextWeek: 'dddd [à] LT',
+ lastDay: '[Hier à] LT',
+ lastWeek: 'dddd [dernier à] LT',
+ sameElse: 'L'
+ },
+ relativeTime : {
+ future : "dans %s",
+ past : "il y a %s",
+ s : "quelques secondes",
+ m : "une minute",
+ mm : "%d minutes",
+ h : "une heure",
+ hh : "%d heures",
+ d : "un jour",
+ dd : "%d jours",
+ M : "un mois",
+ MM : "%d mois",
+ y : "un an",
+ yy : "%d ans"
+ },
+ ordinal : function (number) {
+ return number + (number === 1 ? 'er' : '');
+ },
+ week : {
+ dow : 1, // Monday is the first day of the week.
+ doy : 4 // The week that contains Jan 4th is the first week of the year.
+ }
+ });
+}));
+
+
diff --git a/app/assets/javascripts/bootstrap/dropdown.js b/app/assets/javascripts/bootstrap/dropdown.js
new file mode 100644
index 0000000..43d7ae3
--- /dev/null
+++ b/app/assets/javascripts/bootstrap/dropdown.js
@@ -0,0 +1,147 @@
+/* ========================================================================
+ * Bootstrap: dropdown.js v3.1.1
+ * http://getbootstrap.com/javascript/#dropdowns
+ * ========================================================================
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+ 'use strict';
+
+ // DROPDOWN CLASS DEFINITION
+ // =========================
+
+ var backdrop = '.dropdown-backdrop'
+ var toggle = '[data-toggle=dropdown]'
+ var Dropdown = function (element) {
+ $(element).on('click.bs.dropdown', this.toggle)
+ }
+
+ Dropdown.prototype.toggle = function (e) {
+ var $this = $(this)
+
+ if ($this.is('.disabled, :disabled')) return
+
+ var $parent = getParent($this)
+ var isActive = $parent.hasClass('open')
+
+ clearMenus()
+
+ if (!isActive) {
+ if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) {
+ // if mobile we use a backdrop because click events don't delegate
+ $('
').insertAfter($(this)).on('click', clearMenus)
+ }
+
+ var relatedTarget = { relatedTarget: this }
+ $parent.trigger(e = $.Event('show.bs.dropdown', relatedTarget))
+
+ if (e.isDefaultPrevented()) return
+
+ $parent
+ .toggleClass('open')
+ .trigger('shown.bs.dropdown', relatedTarget)
+
+ $this.focus()
+ }
+
+ return false
+ }
+
+ Dropdown.prototype.keydown = function (e) {
+ if (!/(38|40|27)/.test(e.keyCode)) return
+
+ var $this = $(this)
+
+ e.preventDefault()
+ e.stopPropagation()
+
+ if ($this.is('.disabled, :disabled')) return
+
+ var $parent = getParent($this)
+ var isActive = $parent.hasClass('open')
+
+ if (!isActive || (isActive && e.keyCode == 27)) {
+ if (e.which == 27) $parent.find(toggle).focus()
+ return $this.click()
+ }
+
+ var desc = ' li:not(.divider):visible a'
+ var $items = $parent.find('[role=menu]' + desc + ', [role=listbox]' + desc)
+
+ if (!$items.length) return
+
+ var index = $items.index($items.filter(':focus'))
+
+ if (e.keyCode == 38 && index > 0) index-- // up
+ if (e.keyCode == 40 && index < $items.length - 1) index++ // down
+ if (!~index) index = 0
+
+ $items.eq(index).focus()
+ }
+
+ function clearMenus(e) {
+ $(backdrop).remove()
+ $(toggle).each(function () {
+ var $parent = getParent($(this))
+ var relatedTarget = { relatedTarget: this }
+ if (!$parent.hasClass('open')) return
+ $parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
+ if (e.isDefaultPrevented()) return
+ $parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
+ })
+ }
+
+ function getParent($this) {
+ var selector = $this.attr('data-target')
+
+ if (!selector) {
+ selector = $this.attr('href')
+ selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
+ }
+
+ var $parent = selector && $(selector)
+
+ return $parent && $parent.length ? $parent : $this.parent()
+ }
+
+
+ // DROPDOWN PLUGIN DEFINITION
+ // ==========================
+
+ var old = $.fn.dropdown
+
+ $.fn.dropdown = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.dropdown')
+
+ if (!data) $this.data('bs.dropdown', (data = new Dropdown(this)))
+ if (typeof option == 'string') data[option].call($this)
+ })
+ }
+
+ $.fn.dropdown.Constructor = Dropdown
+
+
+ // DROPDOWN NO CONFLICT
+ // ====================
+
+ $.fn.dropdown.noConflict = function () {
+ $.fn.dropdown = old
+ return this
+ }
+
+
+ // APPLY TO STANDARD DROPDOWN ELEMENTS
+ // ===================================
+
+ $(document)
+ .on('click.bs.dropdown.data-api', clearMenus)
+ .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })
+ .on('click.bs.dropdown.data-api', toggle, Dropdown.prototype.toggle)
+ .on('keydown.bs.dropdown.data-api', toggle + ', [role=menu], [role=listbox]', Dropdown.prototype.keydown)
+
+}(jQuery);
diff --git a/app/assets/javascripts/bootstrap/modal.js b/app/assets/javascripts/bootstrap/modal.js
new file mode 100644
index 0000000..20ff270
--- /dev/null
+++ b/app/assets/javascripts/bootstrap/modal.js
@@ -0,0 +1,243 @@
+/* ========================================================================
+ * Bootstrap: modal.js v3.1.1
+ * http://getbootstrap.com/javascript/#modals
+ * ========================================================================
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+ 'use strict';
+
+ // MODAL CLASS DEFINITION
+ // ======================
+
+ var Modal = function (element, options) {
+ this.options = options
+ this.$element = $(element)
+ this.$backdrop =
+ this.isShown = null
+
+ if (this.options.remote) {
+ this.$element
+ .find('.modal-content')
+ .load(this.options.remote, $.proxy(function () {
+ this.$element.trigger('loaded.bs.modal')
+ }, this))
+ }
+ }
+
+ Modal.DEFAULTS = {
+ backdrop: true,
+ keyboard: true,
+ show: true
+ }
+
+ Modal.prototype.toggle = function (_relatedTarget) {
+ return this[!this.isShown ? 'show' : 'hide'](_relatedTarget)
+ }
+
+ Modal.prototype.show = function (_relatedTarget) {
+ var that = this
+ var e = $.Event('show.bs.modal', { relatedTarget: _relatedTarget })
+
+ this.$element.trigger(e)
+
+ if (this.isShown || e.isDefaultPrevented()) return
+
+ this.isShown = true
+
+ this.escape()
+
+ this.$element.on('click.dismiss.bs.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this))
+
+ this.backdrop(function () {
+ var transition = $.support.transition && that.$element.hasClass('fade')
+
+ if (!that.$element.parent().length) {
+ that.$element.appendTo(document.body) // don't move modals dom position
+ }
+
+ that.$element
+ .show()
+ .scrollTop(0)
+
+ if (transition) {
+ that.$element[0].offsetWidth // force reflow
+ }
+
+ that.$element
+ .addClass('in')
+ .attr('aria-hidden', false)
+
+ that.enforceFocus()
+
+ var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget })
+
+ transition ?
+ that.$element.find('.modal-dialog') // wait for modal to slide in
+ .one($.support.transition.end, function () {
+ that.$element.focus().trigger(e)
+ })
+ .emulateTransitionEnd(300) :
+ that.$element.focus().trigger(e)
+ })
+ }
+
+ Modal.prototype.hide = function (e) {
+ if (e) e.preventDefault()
+
+ e = $.Event('hide.bs.modal')
+
+ this.$element.trigger(e)
+
+ if (!this.isShown || e.isDefaultPrevented()) return
+
+ this.isShown = false
+
+ this.escape()
+
+ $(document).off('focusin.bs.modal')
+
+ this.$element
+ .removeClass('in')
+ .attr('aria-hidden', true)
+ .off('click.dismiss.bs.modal')
+
+ $.support.transition && this.$element.hasClass('fade') ?
+ this.$element
+ .one($.support.transition.end, $.proxy(this.hideModal, this))
+ .emulateTransitionEnd(300) :
+ this.hideModal()
+ }
+
+ Modal.prototype.enforceFocus = function () {
+ $(document)
+ .off('focusin.bs.modal') // guard against infinite focus loop
+ .on('focusin.bs.modal', $.proxy(function (e) {
+ if (this.$element[0] !== e.target && !this.$element.has(e.target).length) {
+ this.$element.focus()
+ }
+ }, this))
+ }
+
+ Modal.prototype.escape = function () {
+ if (this.isShown && this.options.keyboard) {
+ this.$element.on('keyup.dismiss.bs.modal', $.proxy(function (e) {
+ e.which == 27 && this.hide()
+ }, this))
+ } else if (!this.isShown) {
+ this.$element.off('keyup.dismiss.bs.modal')
+ }
+ }
+
+ Modal.prototype.hideModal = function () {
+ var that = this
+ this.$element.hide()
+ this.backdrop(function () {
+ that.removeBackdrop()
+ that.$element.trigger('hidden.bs.modal')
+ })
+ }
+
+ Modal.prototype.removeBackdrop = function () {
+ this.$backdrop && this.$backdrop.remove()
+ this.$backdrop = null
+ }
+
+ Modal.prototype.backdrop = function (callback) {
+ var animate = this.$element.hasClass('fade') ? 'fade' : ''
+
+ if (this.isShown && this.options.backdrop) {
+ var doAnimate = $.support.transition && animate
+
+ this.$backdrop = $('
')
+ .appendTo(document.body)
+
+ this.$element.on('click.dismiss.bs.modal', $.proxy(function (e) {
+ if (e.target !== e.currentTarget) return
+ this.options.backdrop == 'static'
+ ? this.$element[0].focus.call(this.$element[0])
+ : this.hide.call(this)
+ }, this))
+
+ if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
+
+ this.$backdrop.addClass('in')
+
+ if (!callback) return
+
+ doAnimate ?
+ this.$backdrop
+ .one($.support.transition.end, callback)
+ .emulateTransitionEnd(150) :
+ callback()
+
+ } else if (!this.isShown && this.$backdrop) {
+ this.$backdrop.removeClass('in')
+
+ $.support.transition && this.$element.hasClass('fade') ?
+ this.$backdrop
+ .one($.support.transition.end, callback)
+ .emulateTransitionEnd(150) :
+ callback()
+
+ } else if (callback) {
+ callback()
+ }
+ }
+
+
+ // MODAL PLUGIN DEFINITION
+ // =======================
+
+ var old = $.fn.modal
+
+ $.fn.modal = function (option, _relatedTarget) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.modal')
+ var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option)
+
+ if (!data) $this.data('bs.modal', (data = new Modal(this, options)))
+ if (typeof option == 'string') data[option](_relatedTarget)
+ else if (options.show) data.show(_relatedTarget)
+ })
+ }
+
+ $.fn.modal.Constructor = Modal
+
+
+ // MODAL NO CONFLICT
+ // =================
+
+ $.fn.modal.noConflict = function () {
+ $.fn.modal = old
+ return this
+ }
+
+
+ // MODAL DATA-API
+ // ==============
+
+ $(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) {
+ var $this = $(this)
+ var href = $this.attr('href')
+ var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) //strip for ie7
+ var option = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data())
+
+ if ($this.is('a')) e.preventDefault()
+
+ $target
+ .modal(option, this)
+ .one('hide', function () {
+ $this.is(':visible') && $this.focus()
+ })
+ })
+
+ $(document)
+ .on('show.bs.modal', '.modal', function () { $(document.body).addClass('modal-open') })
+ .on('hidden.bs.modal', '.modal', function () { $(document.body).removeClass('modal-open') })
+
+}(jQuery);
diff --git a/app/assets/javascripts/bootstrap/popover.js b/app/assets/javascripts/bootstrap/popover.js
new file mode 100644
index 0000000..23aa829
--- /dev/null
+++ b/app/assets/javascripts/bootstrap/popover.js
@@ -0,0 +1,110 @@
+/* ========================================================================
+ * Bootstrap: popover.js v3.1.1
+ * http://getbootstrap.com/javascript/#popovers
+ * ========================================================================
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+ 'use strict';
+
+ // POPOVER PUBLIC CLASS DEFINITION
+ // ===============================
+
+ var Popover = function (element, options) {
+ this.init('popover', element, options)
+ }
+
+ if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js')
+
+ Popover.DEFAULTS = $.extend({}, $.fn.tooltip.Constructor.DEFAULTS, {
+ placement: 'right',
+ trigger: 'click',
+ content: '',
+ template: ''
+ })
+
+
+ // NOTE: POPOVER EXTENDS tooltip.js
+ // ================================
+
+ Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype)
+
+ Popover.prototype.constructor = Popover
+
+ Popover.prototype.getDefaults = function () {
+ return Popover.DEFAULTS
+ }
+
+ Popover.prototype.setContent = function () {
+ var $tip = this.tip()
+ var title = this.getTitle()
+ var content = this.getContent()
+
+ $tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title)
+ $tip.find('.popover-content')[ // we use append for html objects to maintain js events
+ this.options.html ? (typeof content == 'string' ? 'html' : 'append') : 'text'
+ ](content)
+
+ $tip.removeClass('fade top bottom left right in')
+
+ // IE8 doesn't accept hiding via the `:empty` pseudo selector, we have to do
+ // this manually by checking the contents.
+ if (!$tip.find('.popover-title').html()) $tip.find('.popover-title').hide()
+ }
+
+ Popover.prototype.hasContent = function () {
+ return this.getTitle() || this.getContent()
+ }
+
+ Popover.prototype.getContent = function () {
+ var $e = this.$element
+ var o = this.options
+
+ return $e.attr('data-content')
+ || (typeof o.content == 'function' ?
+ o.content.call($e[0]) :
+ o.content)
+ }
+
+ Popover.prototype.arrow = function () {
+ return this.$arrow = this.$arrow || this.tip().find('.arrow')
+ }
+
+ Popover.prototype.tip = function () {
+ if (!this.$tip) this.$tip = $(this.options.template)
+ return this.$tip
+ }
+
+
+ // POPOVER PLUGIN DEFINITION
+ // =========================
+
+ var old = $.fn.popover
+
+ $.fn.popover = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.popover')
+ var options = typeof option == 'object' && option
+
+ if (!data && option == 'destroy') return
+ if (!data) $this.data('bs.popover', (data = new Popover(this, options)))
+ if (typeof option == 'string') data[option]()
+ })
+ }
+
+ $.fn.popover.Constructor = Popover
+
+
+ // POPOVER NO CONFLICT
+ // ===================
+
+ $.fn.popover.noConflict = function () {
+ $.fn.popover = old
+ return this
+ }
+
+}(jQuery);
diff --git a/app/assets/javascripts/bootstrap/scrollspy.js b/app/assets/javascripts/bootstrap/scrollspy.js
new file mode 100644
index 0000000..4346c86
--- /dev/null
+++ b/app/assets/javascripts/bootstrap/scrollspy.js
@@ -0,0 +1,153 @@
+/* ========================================================================
+ * Bootstrap: scrollspy.js v3.1.1
+ * http://getbootstrap.com/javascript/#scrollspy
+ * ========================================================================
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+ 'use strict';
+
+ // SCROLLSPY CLASS DEFINITION
+ // ==========================
+
+ function ScrollSpy(element, options) {
+ var href
+ var process = $.proxy(this.process, this)
+
+ this.$element = $(element).is('body') ? $(window) : $(element)
+ this.$body = $('body')
+ this.$scrollElement = this.$element.on('scroll.bs.scroll-spy.data-api', process)
+ this.options = $.extend({}, ScrollSpy.DEFAULTS, options)
+ this.selector = (this.options.target
+ || ((href = $(element).attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
+ || '') + ' .nav li > a'
+ this.offsets = $([])
+ this.targets = $([])
+ this.activeTarget = null
+
+ this.refresh()
+ this.process()
+ }
+
+ ScrollSpy.DEFAULTS = {
+ offset: 10
+ }
+
+ ScrollSpy.prototype.refresh = function () {
+ var offsetMethod = this.$element[0] == window ? 'offset' : 'position'
+
+ this.offsets = $([])
+ this.targets = $([])
+
+ var self = this
+ var $targets = this.$body
+ .find(this.selector)
+ .map(function () {
+ var $el = $(this)
+ var href = $el.data('target') || $el.attr('href')
+ var $href = /^#./.test(href) && $(href)
+
+ return ($href
+ && $href.length
+ && $href.is(':visible')
+ && [[ $href[offsetMethod]().top + (!$.isWindow(self.$scrollElement.get(0)) && self.$scrollElement.scrollTop()), href ]]) || null
+ })
+ .sort(function (a, b) { return a[0] - b[0] })
+ .each(function () {
+ self.offsets.push(this[0])
+ self.targets.push(this[1])
+ })
+ }
+
+ ScrollSpy.prototype.process = function () {
+ var scrollTop = this.$scrollElement.scrollTop() + this.options.offset
+ var scrollHeight = this.$scrollElement[0].scrollHeight || this.$body[0].scrollHeight
+ var maxScroll = scrollHeight - this.$scrollElement.height()
+ var offsets = this.offsets
+ var targets = this.targets
+ var activeTarget = this.activeTarget
+ var i
+
+ if (scrollTop >= maxScroll) {
+ return activeTarget != (i = targets.last()[0]) && this.activate(i)
+ }
+
+ if (activeTarget && scrollTop <= offsets[0]) {
+ return activeTarget != (i = targets[0]) && this.activate(i)
+ }
+
+ for (i = offsets.length; i--;) {
+ activeTarget != targets[i]
+ && scrollTop >= offsets[i]
+ && (!offsets[i + 1] || scrollTop <= offsets[i + 1])
+ && this.activate( targets[i] )
+ }
+ }
+
+ ScrollSpy.prototype.activate = function (target) {
+ this.activeTarget = target
+
+ $(this.selector)
+ .parentsUntil(this.options.target, '.active')
+ .removeClass('active')
+
+ var selector = this.selector +
+ '[data-target="' + target + '"],' +
+ this.selector + '[href="' + target + '"]'
+
+ var active = $(selector)
+ .parents('li')
+ .addClass('active')
+
+ if (active.parent('.dropdown-menu').length) {
+ active = active
+ .closest('li.dropdown')
+ .addClass('active')
+ }
+
+ active.trigger('activate.bs.scrollspy')
+ }
+
+
+ // SCROLLSPY PLUGIN DEFINITION
+ // ===========================
+
+ var old = $.fn.scrollspy
+
+ $.fn.scrollspy = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.scrollspy')
+ var options = typeof option == 'object' && option
+
+ if (!data) $this.data('bs.scrollspy', (data = new ScrollSpy(this, options)))
+ if (typeof option == 'string') data[option]()
+ })
+ }
+
+ $.fn.scrollspy.Constructor = ScrollSpy
+
+
+ // SCROLLSPY NO CONFLICT
+ // =====================
+
+ $.fn.scrollspy.noConflict = function () {
+ $.fn.scrollspy = old
+ return this
+ }
+
+
+ // SCROLLSPY DATA-API
+ // ==================
+
+ $(window).on('load', function () {
+ $('[data-spy="scroll"]').each(function () {
+ var $spy = $(this)
+ $spy.scrollspy($spy.data())
+ })
+ })
+
+}(jQuery);
diff --git a/app/assets/javascripts/bootstrap/tab.js b/app/assets/javascripts/bootstrap/tab.js
new file mode 100644
index 0000000..400cb7b
--- /dev/null
+++ b/app/assets/javascripts/bootstrap/tab.js
@@ -0,0 +1,125 @@
+/* ========================================================================
+ * Bootstrap: tab.js v3.1.1
+ * http://getbootstrap.com/javascript/#tabs
+ * ========================================================================
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+ 'use strict';
+
+ // TAB CLASS DEFINITION
+ // ====================
+
+ var Tab = function (element) {
+ this.element = $(element)
+ }
+
+ Tab.prototype.show = function () {
+ var $this = this.element
+ var $ul = $this.closest('ul:not(.dropdown-menu)')
+ var selector = $this.data('target')
+
+ if (!selector) {
+ selector = $this.attr('href')
+ selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
+ }
+
+ if ($this.parent('li').hasClass('active')) return
+
+ var previous = $ul.find('.active:last a')[0]
+ var e = $.Event('show.bs.tab', {
+ relatedTarget: previous
+ })
+
+ $this.trigger(e)
+
+ if (e.isDefaultPrevented()) return
+
+ var $target = $(selector)
+
+ this.activate($this.parent('li'), $ul)
+ this.activate($target, $target.parent(), function () {
+ $this.trigger({
+ type: 'shown.bs.tab',
+ relatedTarget: previous
+ })
+ })
+ }
+
+ Tab.prototype.activate = function (element, container, callback) {
+ var $active = container.find('> .active')
+ var transition = callback
+ && $.support.transition
+ && $active.hasClass('fade')
+
+ function next() {
+ $active
+ .removeClass('active')
+ .find('> .dropdown-menu > .active')
+ .removeClass('active')
+
+ element.addClass('active')
+
+ if (transition) {
+ element[0].offsetWidth // reflow for transition
+ element.addClass('in')
+ } else {
+ element.removeClass('fade')
+ }
+
+ if (element.parent('.dropdown-menu')) {
+ element.closest('li.dropdown').addClass('active')
+ }
+
+ callback && callback()
+ }
+
+ transition ?
+ $active
+ .one($.support.transition.end, next)
+ .emulateTransitionEnd(150) :
+ next()
+
+ $active.removeClass('in')
+ }
+
+
+ // TAB PLUGIN DEFINITION
+ // =====================
+
+ var old = $.fn.tab
+
+ $.fn.tab = function ( option ) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.tab')
+
+ if (!data) $this.data('bs.tab', (data = new Tab(this)))
+ if (typeof option == 'string') data[option]()
+ })
+ }
+
+ $.fn.tab.Constructor = Tab
+
+
+ // TAB NO CONFLICT
+ // ===============
+
+ $.fn.tab.noConflict = function () {
+ $.fn.tab = old
+ return this
+ }
+
+
+ // TAB DATA-API
+ // ============
+
+ $(document).on('click.bs.tab.data-api', '[data-toggle="tab"], [data-toggle="pill"]', function (e) {
+ e.preventDefault()
+ $(this).tab('show')
+ })
+
+}(jQuery);
diff --git a/app/assets/javascripts/bootstrap/tooltip.js b/app/assets/javascripts/bootstrap/tooltip.js
new file mode 100644
index 0000000..f6c0a37
--- /dev/null
+++ b/app/assets/javascripts/bootstrap/tooltip.js
@@ -0,0 +1,399 @@
+/* ========================================================================
+ * Bootstrap: tooltip.js v3.1.1
+ * http://getbootstrap.com/javascript/#tooltip
+ * Inspired by the original jQuery.tipsy by Jason Frame
+ * ========================================================================
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+ 'use strict';
+
+ // TOOLTIP PUBLIC CLASS DEFINITION
+ // ===============================
+
+ var Tooltip = function (element, options) {
+ this.type =
+ this.options =
+ this.enabled =
+ this.timeout =
+ this.hoverState =
+ this.$element = null
+
+ this.init('tooltip', element, options)
+ }
+
+ Tooltip.DEFAULTS = {
+ animation: true,
+ placement: 'top',
+ selector: false,
+ template: '',
+ trigger: 'hover focus',
+ title: '',
+ delay: 0,
+ html: false,
+ container: false
+ }
+
+ Tooltip.prototype.init = function (type, element, options) {
+ this.enabled = true
+ this.type = type
+ this.$element = $(element)
+ this.options = this.getOptions(options)
+
+ var triggers = this.options.trigger.split(' ')
+
+ for (var i = triggers.length; i--;) {
+ var trigger = triggers[i]
+
+ if (trigger == 'click') {
+ this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this))
+ } else if (trigger != 'manual') {
+ var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
+ var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
+
+ this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this))
+ this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this))
+ }
+ }
+
+ this.options.selector ?
+ (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
+ this.fixTitle()
+ }
+
+ Tooltip.prototype.getDefaults = function () {
+ return Tooltip.DEFAULTS
+ }
+
+ Tooltip.prototype.getOptions = function (options) {
+ options = $.extend({}, this.getDefaults(), this.$element.data(), options)
+
+ if (options.delay && typeof options.delay == 'number') {
+ options.delay = {
+ show: options.delay,
+ hide: options.delay
+ }
+ }
+
+ return options
+ }
+
+ Tooltip.prototype.getDelegateOptions = function () {
+ var options = {}
+ var defaults = this.getDefaults()
+
+ this._options && $.each(this._options, function (key, value) {
+ if (defaults[key] != value) options[key] = value
+ })
+
+ return options
+ }
+
+ Tooltip.prototype.enter = function (obj) {
+ var self = obj instanceof this.constructor ?
+ obj : $(obj.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type)
+
+ clearTimeout(self.timeout)
+
+ self.hoverState = 'in'
+
+ if (!self.options.delay || !self.options.delay.show) return self.show()
+
+ self.timeout = setTimeout(function () {
+ if (self.hoverState == 'in') self.show()
+ }, self.options.delay.show)
+ }
+
+ Tooltip.prototype.leave = function (obj) {
+ var self = obj instanceof this.constructor ?
+ obj : $(obj.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type)
+
+ clearTimeout(self.timeout)
+
+ self.hoverState = 'out'
+
+ if (!self.options.delay || !self.options.delay.hide) return self.hide()
+
+ self.timeout = setTimeout(function () {
+ if (self.hoverState == 'out') self.hide()
+ }, self.options.delay.hide)
+ }
+
+ Tooltip.prototype.show = function () {
+ var e = $.Event('show.bs.' + this.type)
+
+ if (this.hasContent() && this.enabled) {
+ this.$element.trigger(e)
+
+ if (e.isDefaultPrevented()) return
+ var that = this;
+
+ var $tip = this.tip()
+
+ this.setContent()
+
+ if (this.options.animation) $tip.addClass('fade')
+
+ var placement = typeof this.options.placement == 'function' ?
+ this.options.placement.call(this, $tip[0], this.$element[0]) :
+ this.options.placement
+
+ var autoToken = /\s?auto?\s?/i
+ var autoPlace = autoToken.test(placement)
+ if (autoPlace) placement = placement.replace(autoToken, '') || 'top'
+
+ $tip
+ .detach()
+ .css({ top: 0, left: 0, display: 'block' })
+ .addClass(placement)
+
+ this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element)
+
+ var pos = this.getPosition()
+ var actualWidth = $tip[0].offsetWidth
+ var actualHeight = $tip[0].offsetHeight
+
+ if (autoPlace) {
+ var $parent = this.$element.parent()
+
+ var orgPlacement = placement
+ var docScroll = document.documentElement.scrollTop || document.body.scrollTop
+ var parentWidth = this.options.container == 'body' ? window.innerWidth : $parent.outerWidth()
+ var parentHeight = this.options.container == 'body' ? window.innerHeight : $parent.outerHeight()
+ var parentLeft = this.options.container == 'body' ? 0 : $parent.offset().left
+
+ placement = placement == 'bottom' && pos.top + pos.height + actualHeight - docScroll > parentHeight ? 'top' :
+ placement == 'top' && pos.top - docScroll - actualHeight < 0 ? 'bottom' :
+ placement == 'right' && pos.right + actualWidth > parentWidth ? 'left' :
+ placement == 'left' && pos.left - actualWidth < parentLeft ? 'right' :
+ placement
+
+ $tip
+ .removeClass(orgPlacement)
+ .addClass(placement)
+ }
+
+ var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight)
+
+ this.applyPlacement(calculatedOffset, placement)
+ this.hoverState = null
+
+ var complete = function() {
+ that.$element.trigger('shown.bs.' + that.type)
+ }
+
+ $.support.transition && this.$tip.hasClass('fade') ?
+ $tip
+ .one($.support.transition.end, complete)
+ .emulateTransitionEnd(150) :
+ complete()
+ }
+ }
+
+ Tooltip.prototype.applyPlacement = function (offset, placement) {
+ var replace
+ var $tip = this.tip()
+ var width = $tip[0].offsetWidth
+ var height = $tip[0].offsetHeight
+
+ // manually read margins because getBoundingClientRect includes difference
+ var marginTop = parseInt($tip.css('margin-top'), 10)
+ var marginLeft = parseInt($tip.css('margin-left'), 10)
+
+ // we must check for NaN for ie 8/9
+ if (isNaN(marginTop)) marginTop = 0
+ if (isNaN(marginLeft)) marginLeft = 0
+
+ offset.top = offset.top + marginTop
+ offset.left = offset.left + marginLeft
+
+ // $.fn.offset doesn't round pixel values
+ // so we use setOffset directly with our own function B-0
+ $.offset.setOffset($tip[0], $.extend({
+ using: function (props) {
+ $tip.css({
+ top: Math.round(props.top),
+ left: Math.round(props.left)
+ })
+ }
+ }, offset), 0)
+
+ $tip.addClass('in')
+
+ // check to see if placing tip in new offset caused the tip to resize itself
+ var actualWidth = $tip[0].offsetWidth
+ var actualHeight = $tip[0].offsetHeight
+
+ if (placement == 'top' && actualHeight != height) {
+ replace = true
+ offset.top = offset.top + height - actualHeight
+ }
+
+ if (/bottom|top/.test(placement)) {
+ var delta = 0
+
+ if (offset.left < 0) {
+ delta = offset.left * -2
+ offset.left = 0
+
+ $tip.offset(offset)
+
+ actualWidth = $tip[0].offsetWidth
+ actualHeight = $tip[0].offsetHeight
+ }
+
+ this.replaceArrow(delta - width + actualWidth, actualWidth, 'left')
+ } else {
+ this.replaceArrow(actualHeight - height, actualHeight, 'top')
+ }
+
+ if (replace) $tip.offset(offset)
+ }
+
+ Tooltip.prototype.replaceArrow = function (delta, dimension, position) {
+ this.arrow().css(position, delta ? (50 * (1 - delta / dimension) + '%') : '')
+ }
+
+ Tooltip.prototype.setContent = function () {
+ var $tip = this.tip()
+ var title = this.getTitle()
+
+ $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title)
+ $tip.removeClass('fade in top bottom left right')
+ }
+
+ Tooltip.prototype.hide = function () {
+ var that = this
+ var $tip = this.tip()
+ var e = $.Event('hide.bs.' + this.type)
+
+ function complete() {
+ if (that.hoverState != 'in') $tip.detach()
+ that.$element.trigger('hidden.bs.' + that.type)
+ }
+
+ this.$element.trigger(e)
+
+ if (e.isDefaultPrevented()) return
+
+ $tip.removeClass('in')
+
+ $.support.transition && this.$tip.hasClass('fade') ?
+ $tip
+ .one($.support.transition.end, complete)
+ .emulateTransitionEnd(150) :
+ complete()
+
+ this.hoverState = null
+
+ return this
+ }
+
+ Tooltip.prototype.fixTitle = function () {
+ var $e = this.$element
+ if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') {
+ $e.attr('data-original-title', $e.attr('title') || '').attr('title', '')
+ }
+ }
+
+ Tooltip.prototype.hasContent = function () {
+ return this.getTitle()
+ }
+
+ Tooltip.prototype.getPosition = function () {
+ var el = this.$element[0]
+ return $.extend({}, (typeof el.getBoundingClientRect == 'function') ? el.getBoundingClientRect() : {
+ width: el.offsetWidth,
+ height: el.offsetHeight
+ }, this.$element.offset())
+ }
+
+ Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) {
+ return placement == 'bottom' ? { top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2 } :
+ placement == 'top' ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 } :
+ placement == 'left' ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } :
+ /* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width }
+ }
+
+ Tooltip.prototype.getTitle = function () {
+ var title
+ var $e = this.$element
+ var o = this.options
+
+ title = $e.attr('data-original-title')
+ || (typeof o.title == 'function' ? o.title.call($e[0]) : o.title)
+
+ return title
+ }
+
+ Tooltip.prototype.tip = function () {
+ return this.$tip = this.$tip || $(this.options.template)
+ }
+
+ Tooltip.prototype.arrow = function () {
+ return this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow')
+ }
+
+ Tooltip.prototype.validate = function () {
+ if (!this.$element[0].parentNode) {
+ this.hide()
+ this.$element = null
+ this.options = null
+ }
+ }
+
+ Tooltip.prototype.enable = function () {
+ this.enabled = true
+ }
+
+ Tooltip.prototype.disable = function () {
+ this.enabled = false
+ }
+
+ Tooltip.prototype.toggleEnabled = function () {
+ this.enabled = !this.enabled
+ }
+
+ Tooltip.prototype.toggle = function (e) {
+ var self = e ? $(e.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type) : this
+ self.tip().hasClass('in') ? self.leave(self) : self.enter(self)
+ }
+
+ Tooltip.prototype.destroy = function () {
+ clearTimeout(this.timeout)
+ this.hide().$element.off('.' + this.type).removeData('bs.' + this.type)
+ }
+
+
+ // TOOLTIP PLUGIN DEFINITION
+ // =========================
+
+ var old = $.fn.tooltip
+
+ $.fn.tooltip = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.tooltip')
+ var options = typeof option == 'object' && option
+
+ if (!data && option == 'destroy') return
+ if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options)))
+ if (typeof option == 'string') data[option]()
+ })
+ }
+
+ $.fn.tooltip.Constructor = Tooltip
+
+
+ // TOOLTIP NO CONFLICT
+ // ===================
+
+ $.fn.tooltip.noConflict = function () {
+ $.fn.tooltip = old
+ return this
+ }
+
+}(jQuery);
diff --git a/app/assets/javascripts/bootstrap/transition.js b/app/assets/javascripts/bootstrap/transition.js
new file mode 100644
index 0000000..efa8c17
--- /dev/null
+++ b/app/assets/javascripts/bootstrap/transition.js
@@ -0,0 +1,48 @@
+/* ========================================================================
+ * Bootstrap: transition.js v3.1.1
+ * http://getbootstrap.com/javascript/#transitions
+ * ========================================================================
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+ 'use strict';
+
+ // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/)
+ // ============================================================
+
+ function transitionEnd() {
+ var el = document.createElement('bootstrap')
+
+ var transEndEventNames = {
+ 'WebkitTransition' : 'webkitTransitionEnd',
+ 'MozTransition' : 'transitionend',
+ 'OTransition' : 'oTransitionEnd otransitionend',
+ 'transition' : 'transitionend'
+ }
+
+ for (var name in transEndEventNames) {
+ if (el.style[name] !== undefined) {
+ return { end: transEndEventNames[name] }
+ }
+ }
+
+ return false // explicit for ie8 ( ._.)
+ }
+
+ // http://blog.alexmaccaw.com/css-transitions
+ $.fn.emulateTransitionEnd = function (duration) {
+ var called = false, $el = this
+ $(this).one($.support.transition.end, function () { called = true })
+ var callback = function () { if (!called) $($el).trigger($.support.transition.end) }
+ setTimeout(callback, duration)
+ return this
+ }
+
+ $(function () {
+ $.support.transition = transitionEnd()
+ })
+
+}(jQuery);
diff --git a/app/assets/javascripts/connexion.coffee b/app/assets/javascripts/connexion.coffee
index 943ed17..38e6279 100644
--- a/app/assets/javascripts/connexion.coffee
+++ b/app/assets/javascripts/connexion.coffee
@@ -1,5 +1,18 @@
#= require jquery
-#= require twitter/bootstrap
+#= require shared/jquery-ui
+#= require bootstrap
#= require ./shared/jquery.backstretch.js
+
+
+resize = () ->
+
+ $(".content").css("margin-top", ($(window).height() - $(".content").outerHeight(false))/ 2)
+
+
+$ ->
+ resize()
+ $(window).bind "resize", ->
+ resize()
+
\ No newline at end of file
diff --git a/app/assets/javascripts/event_form.coffee b/app/assets/javascripts/event_form.coffee
new file mode 100644
index 0000000..9271bba
--- /dev/null
+++ b/app/assets/javascripts/event_form.coffee
@@ -0,0 +1,17 @@
+
+
+@update_event_form = ->
+ if $("#event_stop_date").is(":checked")
+ $(".stop_at").show()
+ else
+ $(".stop_at").hide()
+ if $("#event_entire_day").is(":checked")
+ $(".event_time").hide()
+ $(".event_time_input").attr "value", ""
+ else
+ $(".event_time").show()
+
+$ ->
+
+ $(document).on 'change', ".event_date_form input:checkbox", ->
+ update_event_form()
\ No newline at end of file
diff --git a/app/assets/javascripts/image_files.js b/app/assets/javascripts/image_files.js
new file mode 100644
index 0000000..d6e8bb4
--- /dev/null
+++ b/app/assets/javascripts/image_files.js
@@ -0,0 +1,191 @@
+var slider_value = 160;
+
+function image_files_load()
+{
+
+ update_multiple_selection_text();
+
+ set_image_files_img_size();
+
+ initialize_slider();
+
+
+}
+
+function set_image_files_img_size(){
+ $('.image_file .img').css('width',slider_value+"px");
+ $('.image_file .img').css('height',slider_value+"px");
+
+}
+
+function update_multiple_selection_text()
+{
+
+ var multiple_ids = multiple_selection_ids()
+ var text = ""
+ if (multiple_ids.length == 0)
+ {
+ text = "Aucune image séléctionnée";
+ }
+ else if(multiple_ids.length == 1)
+ {
+ text= "Une image séléctionnée";
+ }
+ else
+ {
+ text= multiple_ids.length+" images séléctionnées";
+ }
+
+
+ $('#multiple_selection_text').html(text);
+
+}
+
+function multiple_selection_ids()
+{
+ var multiple_selection_ids = []
+
+ $('#image_files .active').each(function ()
+ {
+ multiple_selection_ids.push($(this).attr("data_id"))
+ });
+
+ return multiple_selection_ids
+}
+
+function close_image_file_container(image_file_id)
+{
+ $('#image_file_container').removeClass("image_file_container_active");
+ $('#image_files_container').css("overflow", "");
+ $('#right_bar').show();
+
+}
+
+
+
+function delete_multiple_images(){
+ if(confirm("Voulez-vous vraiment supprimer ces images ?"))
+ {
+
+ var multiple_ids = multiple_selection_ids();
+ $.each(multiple_ids, function(index, value)
+ {
+ $.ajax({
+ url:"/admin/image_files/"+value+".js",
+ type : "DELETE",
+ success : function (){
+ update_multiple_selection_text();
+ }
+ });
+ }
+ );
+
+ return false;
+
+ }
+
+}
+
+function select_all_image(){
+ $('#image_files .image_file').each(function (){
+
+
+ $(this).addClass("active");
+
+
+ });
+ update_multiple_selection_text();
+
+}
+
+function unselect_all_image(){
+
+ $('#image_files .image_file').each(function (){
+
+
+ $(this).removeClass("active");
+
+
+ });
+ update_multiple_selection_text();
+}
+
+
+function initialize_slider(){
+
+ $("#grid_slider").slider({
+ value: slider_value,
+ max: 250,
+ min: 80,
+ tooltip:"hide"
+ }).on("slide", function(ev)
+ {
+ slider_value = ev.value
+ set_image_files_img_size();
+ });
+ set_image_files_img_size();
+
+}
+
+$(document).on("dblclick",".image_file", function (event){
+
+
+ $.ajax({url : $(this).attr('data_show_url'), success: function (){
+ $('#image_file_container').addClass("image_file_container_active");
+ $('#right_bar').hide();
+
+ }});
+
+
+});
+
+$(document).on("click","#image_files_container", function (e){
+
+
+
+
+ if ($(e.target).parents(".image_file").length == 0){
+ unselect_all_image();
+
+ }
+
+});
+
+$(document).on("dblclick","#image_file_container .img", function (){
+ close_image_file_container();
+
+});
+
+$(document).on("click",".image_file", function (){
+
+ if ($(this).hasClass('active')){
+ $(this).removeClass('active');
+
+ }
+ else
+ {
+ $(this).addClass('active');
+ }
+ update_multiple_selection_text();
+
+});
+
+$(document).ready(function ($) {
+
+ $("#left_buttons").on("click", function() {
+ multiple_selection_ids();
+ })
+
+ $('#image_files_big_container #main_workspace_view #image_file_container .form input').on('keydown', function(){
+ $('#image_files_big_container #main_workspace_view #image_file_container .form .submit_tr').show();
+ })
+ $('#image_files_big_container #main_workspace_view #image_file_container .form textarea').on('keydown', function(){
+ $('#image_files_big_container #main_workspace_view #image_file_container .form .submit_tr').show();
+ })
+
+ initialize_slider();
+
+ image_files_load();
+
+
+});
\ No newline at end of file
diff --git a/app/assets/javascripts/jquery.bxslider.js b/app/assets/javascripts/jquery.bxslider.js
new file mode 100644
index 0000000..c0413df
--- /dev/null
+++ b/app/assets/javascripts/jquery.bxslider.js
@@ -0,0 +1,1343 @@
+/**
+ * BxSlider v4.1.2 - Fully loaded, responsive content slider
+ * http://bxslider.com
+ *
+ * Copyright 2014, Steven Wanderski - http://stevenwanderski.com - http://bxcreative.com
+ * Written while drinking Belgian ales and listening to jazz
+ *
+ * Released under the MIT license - http://opensource.org/licenses/MIT
+ */
+
+;(function($){
+
+ var plugin = {};
+
+ var defaults = {
+
+ // GENERAL
+ mode: 'horizontal',
+ slideSelector: '',
+ infiniteLoop: true,
+ hideControlOnEnd: false,
+ speed: 500,
+ easing: null,
+ slideMargin: 0,
+ startSlide: 0,
+ randomStart: false,
+ captions: false,
+ ticker: false,
+ tickerHover: false,
+ adaptiveHeight: false,
+ adaptiveHeightSpeed: 500,
+ video: false,
+ useCSS: true,
+ preloadImages: 'visible',
+ responsive: true,
+ slideZIndex: 50,
+ wrapperClass: 'bx-wrapper',
+
+ // TOUCH
+ touchEnabled: true,
+ swipeThreshold: 50,
+ oneToOneTouch: true,
+ preventDefaultSwipeX: true,
+ preventDefaultSwipeY: false,
+
+ // PAGER
+ pager: true,
+ pagerType: 'full',
+ pagerShortSeparator: ' / ',
+ pagerSelector: null,
+ buildPager: null,
+ pagerCustom: null,
+
+ // CONTROLS
+ controls: true,
+ nextText: 'Next',
+ prevText: 'Prev',
+ nextSelector: null,
+ prevSelector: null,
+ autoControls: false,
+ startText: 'Start',
+ stopText: 'Stop',
+ autoControlsCombine: false,
+ autoControlsSelector: null,
+
+ // AUTO
+ auto: false,
+ pause: 4000,
+ autoStart: true,
+ autoDirection: 'next',
+ autoHover: false,
+ autoDelay: 0,
+ autoSlideForOnePage: false,
+
+ // CAROUSEL
+ minSlides: 1,
+ maxSlides: 1,
+ moveSlides: 0,
+ slideWidth: 0,
+
+ // CALLBACKS
+ onSliderLoad: function() {},
+ onSlideBefore: function() {},
+ onSlideAfter: function() {},
+ onSlideNext: function() {},
+ onSlidePrev: function() {},
+ onSliderResize: function() {}
+ }
+
+ $.fn.bxSlider = function(options){
+
+ if(this.length == 0) return this;
+
+ // support mutltiple elements
+ if(this.length > 1){
+ this.each(function(){$(this).bxSlider(options)});
+ return this;
+ }
+
+ // create a namespace to be used throughout the plugin
+ var slider = {};
+ // set a reference to our slider element
+ var el = this;
+ plugin.el = this;
+
+ /**
+ * Makes slideshow responsive
+ */
+ // first get the original window dimens (thanks alot IE)
+ var windowWidth = $(window).width();
+ var windowHeight = $(window).height();
+
+
+
+ /**
+ * ===================================================================================
+ * = PRIVATE FUNCTIONS
+ * ===================================================================================
+ */
+
+ /**
+ * Initializes namespace settings to be used throughout plugin
+ */
+ var init = function(){
+ // merge user-supplied options with the defaults
+ slider.settings = $.extend({}, defaults, options);
+ // parse slideWidth setting
+ slider.settings.slideWidth = parseInt(slider.settings.slideWidth);
+ // store the original children
+ slider.children = el.children(slider.settings.slideSelector);
+ // check if actual number of slides is less than minSlides / maxSlides
+ if(slider.children.length < slider.settings.minSlides) slider.settings.minSlides = slider.children.length;
+ if(slider.children.length < slider.settings.maxSlides) slider.settings.maxSlides = slider.children.length;
+ // if random start, set the startSlide setting to random number
+ if(slider.settings.randomStart) slider.settings.startSlide = Math.floor(Math.random() * slider.children.length);
+ // store active slide information
+ slider.active = { index: slider.settings.startSlide }
+ // store if the slider is in carousel mode (displaying / moving multiple slides)
+ slider.carousel = slider.settings.minSlides > 1 || slider.settings.maxSlides > 1;
+ // if carousel, force preloadImages = 'all'
+ if(slider.carousel) slider.settings.preloadImages = 'all';
+ // calculate the min / max width thresholds based on min / max number of slides
+ // used to setup and update carousel slides dimensions
+ slider.minThreshold = (slider.settings.minSlides * slider.settings.slideWidth) + ((slider.settings.minSlides - 1) * slider.settings.slideMargin);
+ slider.maxThreshold = (slider.settings.maxSlides * slider.settings.slideWidth) + ((slider.settings.maxSlides - 1) * slider.settings.slideMargin);
+ // store the current state of the slider (if currently animating, working is true)
+ slider.working = false;
+ // initialize the controls object
+ slider.controls = {};
+ // initialize an auto interval
+ slider.interval = null;
+ // determine which property to use for transitions
+ slider.animProp = slider.settings.mode == 'vertical' ? 'top' : 'left';
+ // determine if hardware acceleration can be used
+ slider.usingCSS = slider.settings.useCSS && slider.settings.mode != 'fade' && (function(){
+ // create our test div element
+ var div = document.createElement('div');
+ // css transition properties
+ var props = ['WebkitPerspective', 'MozPerspective', 'OPerspective', 'msPerspective'];
+ // test for each property
+ for(var i in props){
+ if(div.style[props[i]] !== undefined){
+ slider.cssPrefix = props[i].replace('Perspective', '').toLowerCase();
+ slider.animProp = '-' + slider.cssPrefix + '-transform';
+ return true;
+ }
+ }
+ return false;
+ }());
+ // if vertical mode always make maxSlides and minSlides equal
+ if(slider.settings.mode == 'vertical') slider.settings.maxSlides = slider.settings.minSlides;
+ // save original style data
+ el.data("origStyle", el.attr("style"));
+ el.children(slider.settings.slideSelector).each(function() {
+ $(this).data("origStyle", $(this).attr("style"));
+ });
+ // perform all DOM / CSS modifications
+ setup();
+ }
+
+ /**
+ * Performs all DOM and CSS modifications
+ */
+ var setup = function(){
+ // wrap el in a wrapper
+ el.wrap('');
+ // store a namspace reference to .bx-viewport
+ slider.viewport = el.parent();
+ // add a loading div to display while images are loading
+ slider.loader = $('
');
+ slider.viewport.prepend(slider.loader);
+ // set el to a massive width, to hold any needed slides
+ // also strip any margin and padding from el
+ el.css({
+ width: slider.settings.mode == 'horizontal' ? (slider.children.length * 100 + 215) + '%' : 'auto',
+ position: 'relative'
+ });
+ // if using CSS, add the easing property
+ if(slider.usingCSS && slider.settings.easing){
+ el.css('-' + slider.cssPrefix + '-transition-timing-function', slider.settings.easing);
+ // if not using CSS and no easing value was supplied, use the default JS animation easing (swing)
+ }else if(!slider.settings.easing){
+ slider.settings.easing = 'swing';
+ }
+ var slidesShowing = getNumberSlidesShowing();
+ // make modifications to the viewport (.bx-viewport)
+ slider.viewport.css({
+ width: '100%',
+ overflow: 'hidden',
+ position: 'relative'
+ });
+ slider.viewport.parent().css({
+ maxWidth: getViewportMaxWidth()
+ });
+ // make modification to the wrapper (.bx-wrapper)
+ if(!slider.settings.pager) {
+ slider.viewport.parent().css({
+ margin: '0 auto 0px'
+ });
+ }
+ // apply css to all slider children
+ slider.children.css({
+ 'float': slider.settings.mode == 'horizontal' ? 'left' : 'none',
+ listStyle: 'none',
+ position: 'relative'
+ });
+ // apply the calculated width after the float is applied to prevent scrollbar interference
+ slider.children.css('width', getSlideWidth());
+ // if slideMargin is supplied, add the css
+ if(slider.settings.mode == 'horizontal' && slider.settings.slideMargin > 0) slider.children.css('marginRight', slider.settings.slideMargin);
+ if(slider.settings.mode == 'vertical' && slider.settings.slideMargin > 0) slider.children.css('marginBottom', slider.settings.slideMargin);
+ // if "fade" mode, add positioning and z-index CSS
+ if(slider.settings.mode == 'fade'){
+ slider.children.css({
+ position: 'absolute',
+ zIndex: 0,
+ display: 'none'
+ });
+ // prepare the z-index on the showing element
+ slider.children.eq(slider.settings.startSlide).css({zIndex: slider.settings.slideZIndex, display: 'block'});
+ }
+ // create an element to contain all slider controls (pager, start / stop, etc)
+ slider.controls.el = $('
');
+ // if captions are requested, add them
+ if(slider.settings.captions) appendCaptions();
+ // check if startSlide is last slide
+ slider.active.last = slider.settings.startSlide == getPagerQty() - 1;
+ // if video is true, set up the fitVids plugin
+ if(slider.settings.video) el.fitVids();
+ // set the default preload selector (visible)
+ var preloadSelector = slider.children.eq(slider.settings.startSlide);
+ if (slider.settings.preloadImages == "all") preloadSelector = slider.children;
+ // only check for control addition if not in "ticker" mode
+ if(!slider.settings.ticker){
+ // if pager is requested, add it
+ if(slider.settings.pager) appendPager();
+ // if controls are requested, add them
+ if(slider.settings.controls) appendControls();
+ // if auto is true, and auto controls are requested, add them
+ if(slider.settings.auto && slider.settings.autoControls) appendControlsAuto();
+ // if any control option is requested, add the controls wrapper
+ if(slider.settings.controls || slider.settings.autoControls || slider.settings.pager) slider.viewport.after(slider.controls.el);
+ // if ticker mode, do not allow a pager
+ }else{
+ slider.settings.pager = false;
+ }
+ // preload all images, then perform final DOM / CSS modifications that depend on images being loaded
+ loadElements(preloadSelector, start);
+ }
+
+ var loadElements = function(selector, callback){
+ var total = selector.find('img, iframe').length;
+ if (total == 0){
+ callback();
+ return;
+ }
+ var count = 0;
+ selector.find('img, iframe').each(function(){
+ $(this).one('load', function() {
+ if(++count == total) callback();
+ }).each(function() {
+ if(this.complete) $(this).load();
+ });
+ });
+ }
+
+ /**
+ * Start the slider
+ */
+ var start = function(){
+ // if infinite loop, prepare additional slides
+ if(slider.settings.infiniteLoop && slider.settings.mode != 'fade' && !slider.settings.ticker){
+ var slice = slider.settings.mode == 'vertical' ? slider.settings.minSlides : slider.settings.maxSlides;
+ var sliceAppend = slider.children.slice(0, slice).clone().addClass('bx-clone');
+ var slicePrepend = slider.children.slice(-slice).clone().addClass('bx-clone');
+ el.append(sliceAppend).prepend(slicePrepend);
+ }
+ // remove the loading DOM element
+ slider.loader.remove();
+ // set the left / top position of "el"
+ setSlidePosition();
+ // if "vertical" mode, always use adaptiveHeight to prevent odd behavior
+ if (slider.settings.mode == 'vertical') slider.settings.adaptiveHeight = true;
+ // set the viewport height
+ slider.viewport.height(getViewportHeight());
+ // make sure everything is positioned just right (same as a window resize)
+ el.redrawSlider();
+ // onSliderLoad callback
+ slider.settings.onSliderLoad(slider.active.index);
+ // slider has been fully initialized
+ slider.initialized = true;
+ // bind the resize call to the window
+ if (slider.settings.responsive) $(window).bind('resize', resizeWindow);
+ // if auto is true and has more than 1 page, start the show
+ if (slider.settings.auto && slider.settings.autoStart && (getPagerQty() > 1 || slider.settings.autoSlideForOnePage)) initAuto();
+ // if ticker is true, start the ticker
+ if (slider.settings.ticker) initTicker();
+ // if pager is requested, make the appropriate pager link active
+ if (slider.settings.pager) updatePagerActive(slider.settings.startSlide);
+ // check for any updates to the controls (like hideControlOnEnd updates)
+ if (slider.settings.controls) updateDirectionControls();
+ // if touchEnabled is true, setup the touch events
+ if (slider.settings.touchEnabled && !slider.settings.ticker) initTouch();
+ }
+
+ /**
+ * Returns the calculated height of the viewport, used to determine either adaptiveHeight or the maxHeight value
+ */
+ var getViewportHeight = function(){
+ var height = 0;
+ // first determine which children (slides) should be used in our height calculation
+ var children = $();
+ // if mode is not "vertical" and adaptiveHeight is false, include all children
+ if(slider.settings.mode != 'vertical' && !slider.settings.adaptiveHeight){
+ children = slider.children;
+ }else{
+ // if not carousel, return the single active child
+ if(!slider.carousel){
+ children = slider.children.eq(slider.active.index);
+ // if carousel, return a slice of children
+ }else{
+ // get the individual slide index
+ var currentIndex = slider.settings.moveSlides == 1 ? slider.active.index : slider.active.index * getMoveBy();
+ // add the current slide to the children
+ children = slider.children.eq(currentIndex);
+ // cycle through the remaining "showing" slides
+ for (i = 1; i <= slider.settings.maxSlides - 1; i++){
+ // if looped back to the start
+ if(currentIndex + i >= slider.children.length){
+ children = children.add(slider.children.eq(i - 1));
+ }else{
+ children = children.add(slider.children.eq(currentIndex + i));
+ }
+ }
+ }
+ }
+ // if "vertical" mode, calculate the sum of the heights of the children
+ if(slider.settings.mode == 'vertical'){
+ children.each(function(index) {
+ height += $(this).outerHeight();
+ });
+ // add user-supplied margins
+ if(slider.settings.slideMargin > 0){
+ height += slider.settings.slideMargin * (slider.settings.minSlides - 1);
+ }
+ // if not "vertical" mode, calculate the max height of the children
+ }else{
+ height = Math.max.apply(Math, children.map(function(){
+ return $(this).outerHeight(false);
+ }).get());
+ }
+
+ if(slider.viewport.css('box-sizing') == 'border-box'){
+ height += parseFloat(slider.viewport.css('padding-top')) + parseFloat(slider.viewport.css('padding-bottom')) +
+ parseFloat(slider.viewport.css('border-top-width')) + parseFloat(slider.viewport.css('border-bottom-width'));
+ }else if(slider.viewport.css('box-sizing') == 'padding-box'){
+ height += parseFloat(slider.viewport.css('padding-top')) + parseFloat(slider.viewport.css('padding-bottom'));
+ }
+
+ return height;
+ }
+
+ /**
+ * Returns the calculated width to be used for the outer wrapper / viewport
+ */
+ var getViewportMaxWidth = function(){
+ var width = '100%';
+ if(slider.settings.slideWidth > 0){
+ if(slider.settings.mode == 'horizontal'){
+ width = (slider.settings.maxSlides * slider.settings.slideWidth) + ((slider.settings.maxSlides - 1) * slider.settings.slideMargin);
+ }else{
+ width = slider.settings.slideWidth;
+ }
+ }
+ return width;
+ }
+
+ /**
+ * Returns the calculated width to be applied to each slide
+ */
+ var getSlideWidth = function(){
+ // start with any user-supplied slide width
+ var newElWidth = slider.settings.slideWidth;
+ // get the current viewport width
+ var wrapWidth = slider.viewport.width();
+ // if slide width was not supplied, or is larger than the viewport use the viewport width
+ if(slider.settings.slideWidth == 0 ||
+ (slider.settings.slideWidth > wrapWidth && !slider.carousel) ||
+ slider.settings.mode == 'vertical'){
+ newElWidth = wrapWidth;
+ // if carousel, use the thresholds to determine the width
+ }else if(slider.settings.maxSlides > 1 && slider.settings.mode == 'horizontal'){
+ if(wrapWidth > slider.maxThreshold){
+ // newElWidth = (wrapWidth - (slider.settings.slideMargin * (slider.settings.maxSlides - 1))) / slider.settings.maxSlides;
+ }else if(wrapWidth < slider.minThreshold){
+ newElWidth = (wrapWidth - (slider.settings.slideMargin * (slider.settings.minSlides - 1))) / slider.settings.minSlides;
+ }
+ }
+ return newElWidth;
+ }
+
+ /**
+ * Returns the number of slides currently visible in the viewport (includes partially visible slides)
+ */
+ var getNumberSlidesShowing = function(){
+ var slidesShowing = 1;
+ if(slider.settings.mode == 'horizontal' && slider.settings.slideWidth > 0){
+ // if viewport is smaller than minThreshold, return minSlides
+ if(slider.viewport.width() < slider.minThreshold){
+ slidesShowing = slider.settings.minSlides;
+ // if viewport is larger than minThreshold, return maxSlides
+ }else if(slider.viewport.width() > slider.maxThreshold){
+ slidesShowing = slider.settings.maxSlides;
+ // if viewport is between min / max thresholds, divide viewport width by first child width
+ }else{
+ var childWidth = slider.children.first().width() + slider.settings.slideMargin;
+ slidesShowing = Math.floor((slider.viewport.width() +
+ slider.settings.slideMargin) / childWidth);
+ }
+ // if "vertical" mode, slides showing will always be minSlides
+ }else if(slider.settings.mode == 'vertical'){
+ slidesShowing = slider.settings.minSlides;
+ }
+ return slidesShowing;
+ }
+
+ /**
+ * Returns the number of pages (one full viewport of slides is one "page")
+ */
+ var getPagerQty = function(){
+ var pagerQty = 0;
+ // if moveSlides is specified by the user
+ if(slider.settings.moveSlides > 0){
+ if(slider.settings.infiniteLoop){
+ pagerQty = Math.ceil(slider.children.length / getMoveBy());
+ }else{
+ // use a while loop to determine pages
+ var breakPoint = 0;
+ var counter = 0
+ // when breakpoint goes above children length, counter is the number of pages
+ while (breakPoint < slider.children.length){
+ ++pagerQty;
+ breakPoint = counter + getNumberSlidesShowing();
+ counter += slider.settings.moveSlides <= getNumberSlidesShowing() ? slider.settings.moveSlides : getNumberSlidesShowing();
+ }
+ }
+ // if moveSlides is 0 (auto) divide children length by sides showing, then round up
+ }else{
+ pagerQty = Math.ceil(slider.children.length / getNumberSlidesShowing());
+ }
+ return pagerQty;
+ }
+
+ /**
+ * Returns the number of indivual slides by which to shift the slider
+ */
+ var getMoveBy = function(){
+ // if moveSlides was set by the user and moveSlides is less than number of slides showing
+ if(slider.settings.moveSlides > 0 && slider.settings.moveSlides <= getNumberSlidesShowing()){
+ return slider.settings.moveSlides;
+ }
+ // if moveSlides is 0 (auto)
+ return getNumberSlidesShowing();
+ }
+
+ /**
+ * Sets the slider's (el) left or top position
+ */
+ var setSlidePosition = function(){
+ // if last slide, not infinite loop, and number of children is larger than specified maxSlides
+ if(slider.children.length > slider.settings.maxSlides && slider.active.last && !slider.settings.infiniteLoop){
+ if (slider.settings.mode == 'horizontal'){
+ // get the last child's position
+ var lastChild = slider.children.last();
+ var position = lastChild.position();
+ // set the left position
+ setPositionProperty(-(position.left - (slider.viewport.width() - lastChild.outerWidth())), 'reset', 0);
+ }else if(slider.settings.mode == 'vertical'){
+ // get the last showing index's position
+ var lastShowingIndex = slider.children.length - slider.settings.minSlides;
+ var position = slider.children.eq(lastShowingIndex).position();
+ // set the top position
+ setPositionProperty(-position.top, 'reset', 0);
+ }
+ // if not last slide
+ }else{
+ // get the position of the first showing slide
+ var position = slider.children.eq(slider.active.index * getMoveBy()).position();
+ // check for last slide
+ if (slider.active.index == getPagerQty() - 1) slider.active.last = true;
+ // set the repective position
+ if (position != undefined){
+ if (slider.settings.mode == 'horizontal') setPositionProperty(-position.left, 'reset', 0);
+ else if (slider.settings.mode == 'vertical') setPositionProperty(-position.top, 'reset', 0);
+ }
+ }
+ }
+
+ /**
+ * Sets the el's animating property position (which in turn will sometimes animate el).
+ * If using CSS, sets the transform property. If not using CSS, sets the top / left property.
+ *
+ * @param value (int)
+ * - the animating property's value
+ *
+ * @param type (string) 'slider', 'reset', 'ticker'
+ * - the type of instance for which the function is being
+ *
+ * @param duration (int)
+ * - the amount of time (in ms) the transition should occupy
+ *
+ * @param params (array) optional
+ * - an optional parameter containing any variables that need to be passed in
+ */
+ var setPositionProperty = function(value, type, duration, params){
+ // use CSS transform
+ if(slider.usingCSS){
+ // determine the translate3d value
+ var propValue = slider.settings.mode == 'vertical' ? 'translate3d(0, ' + value + 'px, 0)' : 'translate3d(' + value + 'px, 0, 0)';
+ // add the CSS transition-duration
+ el.css('-' + slider.cssPrefix + '-transition-duration', duration / 1000 + 's');
+ if(type == 'slide'){
+ // set the property value
+ el.css(slider.animProp, propValue);
+ // bind a callback method - executes when CSS transition completes
+ el.bind('transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd', function(){
+ // unbind the callback
+ el.unbind('transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd');
+ updateAfterSlideTransition();
+ });
+ }else if(type == 'reset'){
+ el.css(slider.animProp, propValue);
+ }else if(type == 'ticker'){
+ // make the transition use 'linear'
+ el.css('-' + slider.cssPrefix + '-transition-timing-function', 'linear');
+ el.css(slider.animProp, propValue);
+ // bind a callback method - executes when CSS transition completes
+ el.bind('transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd', function(){
+ // unbind the callback
+ el.unbind('transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd');
+ // reset the position
+ setPositionProperty(params['resetValue'], 'reset', 0);
+ // start the loop again
+ tickerLoop();
+ });
+ }
+ // use JS animate
+ }else{
+ var animateObj = {};
+ animateObj[slider.animProp] = value;
+ if(type == 'slide'){
+ el.animate(animateObj, duration, slider.settings.easing, function(){
+ updateAfterSlideTransition();
+ });
+ }else if(type == 'reset'){
+ el.css(slider.animProp, value)
+ }else if(type == 'ticker'){
+ el.animate(animateObj, speed, 'linear', function(){
+ setPositionProperty(params['resetValue'], 'reset', 0);
+ // run the recursive loop after animation
+ tickerLoop();
+ });
+ }
+ }
+ }
+
+ /**
+ * Populates the pager with proper amount of pages
+ */
+ var populatePager = function(){
+ var pagerHtml = '';
+ var pagerQty = getPagerQty();
+ // loop through each pager item
+ for(var i=0; i < pagerQty; i++){
+ var linkContent = '';
+ // if a buildPager function is supplied, use it to get pager link value, else use index + 1
+ if(slider.settings.buildPager && $.isFunction(slider.settings.buildPager)){
+ linkContent = slider.settings.buildPager(i);
+ slider.pagerEl.addClass('bx-custom-pager');
+ }else{
+ linkContent = i + 1;
+ slider.pagerEl.addClass('bx-default-pager');
+ }
+ // var linkContent = slider.settings.buildPager && $.isFunction(slider.settings.buildPager) ? slider.settings.buildPager(i) : i + 1;
+ // add the markup to the string
+ pagerHtml += '';
+ };
+ // populate the pager element with pager links
+ slider.pagerEl.html(pagerHtml);
+ }
+
+ /**
+ * Appends the pager to the controls element
+ */
+ var appendPager = function(){
+ if(!slider.settings.pagerCustom){
+ // create the pager DOM element
+ slider.pagerEl = $('');
+ // if a pager selector was supplied, populate it with the pager
+ if(slider.settings.pagerSelector){
+ $(slider.settings.pagerSelector).html(slider.pagerEl);
+ // if no pager selector was supplied, add it after the wrapper
+ }else{
+ slider.controls.el.addClass('bx-has-pager').append(slider.pagerEl);
+ }
+ // populate the pager
+ populatePager();
+ }else{
+ slider.pagerEl = $(slider.settings.pagerCustom);
+ }
+ // assign the pager click binding
+ slider.pagerEl.on('click', 'a', clickPagerBind);
+ }
+
+ /**
+ * Appends prev / next controls to the controls element
+ */
+ var appendControls = function(){
+ slider.controls.next = $(' ' + slider.settings.nextText + ' ');
+ slider.controls.prev = $('' + slider.settings.prevText + ' ');
+ // bind click actions to the controls
+ slider.controls.next.bind('click', clickNextBind);
+ slider.controls.prev.bind('click', clickPrevBind);
+ // if nextSlector was supplied, populate it
+ if(slider.settings.nextSelector){
+ $(slider.settings.nextSelector).append(slider.controls.next);
+ }
+ // if prevSlector was supplied, populate it
+ if(slider.settings.prevSelector){
+ $(slider.settings.prevSelector).append(slider.controls.prev);
+ }
+ // if no custom selectors were supplied
+ if(!slider.settings.nextSelector && !slider.settings.prevSelector){
+ // add the controls to the DOM
+ slider.controls.directionEl = $('
');
+ // add the control elements to the directionEl
+ slider.controls.directionEl.append(slider.controls.prev).append(slider.controls.next);
+ // slider.viewport.append(slider.controls.directionEl);
+ slider.controls.el.addClass('bx-has-controls-direction').append(slider.controls.directionEl);
+ }
+ }
+
+ /**
+ * Appends start / stop auto controls to the controls element
+ */
+ var appendControlsAuto = function(){
+ slider.controls.start = $('');
+ slider.controls.stop = $('');
+ // add the controls to the DOM
+ slider.controls.autoEl = $('
');
+ // bind click actions to the controls
+ slider.controls.autoEl.on('click', '.bx-start', clickStartBind);
+ slider.controls.autoEl.on('click', '.bx-stop', clickStopBind);
+ // if autoControlsCombine, insert only the "start" control
+ if(slider.settings.autoControlsCombine){
+ slider.controls.autoEl.append(slider.controls.start);
+ // if autoControlsCombine is false, insert both controls
+ }else{
+ slider.controls.autoEl.append(slider.controls.start).append(slider.controls.stop);
+ }
+ // if auto controls selector was supplied, populate it with the controls
+ if(slider.settings.autoControlsSelector){
+ $(slider.settings.autoControlsSelector).html(slider.controls.autoEl);
+ // if auto controls selector was not supplied, add it after the wrapper
+ }else{
+ slider.controls.el.addClass('bx-has-controls-auto').append(slider.controls.autoEl);
+ }
+ // update the auto controls
+ updateAutoControls(slider.settings.autoStart ? 'stop' : 'start');
+ }
+
+ /**
+ * Appends image captions to the DOM
+ */
+ var appendCaptions = function(){
+ // cycle through each child
+ slider.children.each(function(index){
+ // get the image title attribute
+ var title = $(this).find('img:first').attr('title');
+ // append the caption
+ if (title != undefined && ('' + title).length) {
+ $(this).append('' + title + '
');
+ }
+ });
+ }
+
+ /**
+ * Click next binding
+ *
+ * @param e (event)
+ * - DOM event object
+ */
+ var clickNextBind = function(e){
+ // if auto show is running, stop it
+ if (slider.settings.auto) el.stopAuto();
+ el.goToNextSlide();
+ e.preventDefault();
+ }
+
+ /**
+ * Click prev binding
+ *
+ * @param e (event)
+ * - DOM event object
+ */
+ var clickPrevBind = function(e){
+ // if auto show is running, stop it
+ if (slider.settings.auto) el.stopAuto();
+ el.goToPrevSlide();
+ e.preventDefault();
+ }
+
+ /**
+ * Click start binding
+ *
+ * @param e (event)
+ * - DOM event object
+ */
+ var clickStartBind = function(e){
+ el.startAuto();
+ e.preventDefault();
+ }
+
+ /**
+ * Click stop binding
+ *
+ * @param e (event)
+ * - DOM event object
+ */
+ var clickStopBind = function(e){
+ el.stopAuto();
+ e.preventDefault();
+ }
+
+ /**
+ * Click pager binding
+ *
+ * @param e (event)
+ * - DOM event object
+ */
+ var clickPagerBind = function(e){
+ // if auto show is running, stop it
+ if (slider.settings.auto) el.stopAuto();
+ var pagerLink = $(e.currentTarget);
+ if(pagerLink.attr('data-slide-index') !== undefined){
+ var pagerIndex = parseInt(pagerLink.attr('data-slide-index'));
+ // if clicked pager link is not active, continue with the goToSlide call
+ if(pagerIndex != slider.active.index) el.goToSlide(pagerIndex);
+ e.preventDefault();
+ }
+ }
+
+ /**
+ * Updates the pager links with an active class
+ *
+ * @param slideIndex (int)
+ * - index of slide to make active
+ */
+ var updatePagerActive = function(slideIndex){
+ // if "short" pager type
+ var len = slider.children.length; // nb of children
+ if(slider.settings.pagerType == 'short'){
+ if(slider.settings.maxSlides > 1) {
+ len = Math.ceil(slider.children.length/slider.settings.maxSlides);
+ }
+ slider.pagerEl.html( (slideIndex + 1) + slider.settings.pagerShortSeparator + len);
+ return;
+ }
+ // remove all pager active classes
+ slider.pagerEl.find('a').removeClass('active');
+ // apply the active class for all pagers
+ slider.pagerEl.each(function(i, el) { $(el).find('a').eq(slideIndex).addClass('active'); });
+ }
+
+ /**
+ * Performs needed actions after a slide transition
+ */
+ var updateAfterSlideTransition = function(){
+ // if infinte loop is true
+ if(slider.settings.infiniteLoop){
+ var position = '';
+ // first slide
+ if(slider.active.index == 0){
+ // set the new position
+ position = slider.children.eq(0).position();
+ // carousel, last slide
+ }else if(slider.active.index == getPagerQty() - 1 && slider.carousel){
+ position = slider.children.eq((getPagerQty() - 1) * getMoveBy()).position();
+ // last slide
+ }else if(slider.active.index == slider.children.length - 1){
+ position = slider.children.eq(slider.children.length - 1).position();
+ }
+ if(position){
+ if (slider.settings.mode == 'horizontal') { setPositionProperty(-position.left, 'reset', 0); }
+ else if (slider.settings.mode == 'vertical') { setPositionProperty(-position.top, 'reset', 0); }
+ }
+ }
+ // declare that the transition is complete
+ slider.working = false;
+ // onSlideAfter callback
+ slider.settings.onSlideAfter(slider.children.eq(slider.active.index), slider.oldIndex, slider.active.index);
+ }
+
+ /**
+ * Updates the auto controls state (either active, or combined switch)
+ *
+ * @param state (string) "start", "stop"
+ * - the new state of the auto show
+ */
+ var updateAutoControls = function(state){
+ // if autoControlsCombine is true, replace the current control with the new state
+ if(slider.settings.autoControlsCombine){
+ slider.controls.autoEl.html(slider.controls[state]);
+ // if autoControlsCombine is false, apply the "active" class to the appropriate control
+ }else{
+ slider.controls.autoEl.find('a').removeClass('active');
+ slider.controls.autoEl.find('a:not(.bx-' + state + ')').addClass('active');
+ }
+ }
+
+ /**
+ * Updates the direction controls (checks if either should be hidden)
+ */
+ var updateDirectionControls = function(){
+ if(getPagerQty() == 1){
+ slider.controls.prev.addClass('disabled');
+ slider.controls.next.addClass('disabled');
+ }else if(!slider.settings.infiniteLoop && slider.settings.hideControlOnEnd){
+ // if first slide
+ if (slider.active.index == 0){
+ slider.controls.prev.addClass('disabled');
+ slider.controls.next.removeClass('disabled');
+ // if last slide
+ }else if(slider.active.index == getPagerQty() - 1){
+ slider.controls.next.addClass('disabled');
+ slider.controls.prev.removeClass('disabled');
+ // if any slide in the middle
+ }else{
+ slider.controls.prev.removeClass('disabled');
+ slider.controls.next.removeClass('disabled');
+ }
+ }
+ }
+
+ /**
+ * Initialzes the auto process
+ */
+ var initAuto = function(){
+ // if autoDelay was supplied, launch the auto show using a setTimeout() call
+ if(slider.settings.autoDelay > 0){
+ var timeout = setTimeout(el.startAuto, slider.settings.autoDelay);
+ // if autoDelay was not supplied, start the auto show normally
+ }else{
+ el.startAuto();
+ }
+ // if autoHover is requested
+ if(slider.settings.autoHover){
+ // on el hover
+ el.hover(function(){
+ // if the auto show is currently playing (has an active interval)
+ if(slider.interval){
+ // stop the auto show and pass true agument which will prevent control update
+ el.stopAuto(true);
+ // create a new autoPaused value which will be used by the relative "mouseout" event
+ slider.autoPaused = true;
+ }
+ }, function(){
+ // if the autoPaused value was created be the prior "mouseover" event
+ if(slider.autoPaused){
+ // start the auto show and pass true agument which will prevent control update
+ el.startAuto(true);
+ // reset the autoPaused value
+ slider.autoPaused = null;
+ }
+ });
+ }
+ }
+
+ /**
+ * Initialzes the ticker process
+ */
+ var initTicker = function(){
+ var startPosition = 0;
+ // if autoDirection is "next", append a clone of the entire slider
+ if(slider.settings.autoDirection == 'next'){
+ el.append(slider.children.clone().addClass('bx-clone'));
+ // if autoDirection is "prev", prepend a clone of the entire slider, and set the left position
+ }else{
+ el.prepend(slider.children.clone().addClass('bx-clone'));
+ var position = slider.children.first().position();
+ startPosition = slider.settings.mode == 'horizontal' ? -position.left : -position.top;
+ }
+ setPositionProperty(startPosition, 'reset', 0);
+ // do not allow controls in ticker mode
+ slider.settings.pager = false;
+ slider.settings.controls = false;
+ slider.settings.autoControls = false;
+ // if autoHover is requested
+ if(slider.settings.tickerHover && !slider.usingCSS){
+ // on el hover
+ slider.viewport.hover(function(){
+ el.stop();
+ }, function(){
+ // calculate the total width of children (used to calculate the speed ratio)
+ var totalDimens = 0;
+ slider.children.each(function(index){
+ totalDimens += slider.settings.mode == 'horizontal' ? $(this).outerWidth(true) : $(this).outerHeight(true);
+ });
+ // calculate the speed ratio (used to determine the new speed to finish the paused animation)
+ var ratio = slider.settings.speed / totalDimens;
+ // determine which property to use
+ var property = slider.settings.mode == 'horizontal' ? 'left' : 'top';
+ // calculate the new speed
+ var newSpeed = ratio * (totalDimens - (Math.abs(parseInt(el.css(property)))));
+ tickerLoop(newSpeed);
+ });
+ }
+ // start the ticker loop
+ tickerLoop();
+ }
+
+ /**
+ * Runs a continuous loop, news ticker-style
+ */
+ var tickerLoop = function(resumeSpeed){
+ speed = resumeSpeed ? resumeSpeed : slider.settings.speed;
+ var position = {left: 0, top: 0};
+ var reset = {left: 0, top: 0};
+ // if "next" animate left position to last child, then reset left to 0
+ if(slider.settings.autoDirection == 'next'){
+ position = el.find('.bx-clone').first().position();
+ // if "prev" animate left position to 0, then reset left to first non-clone child
+ }else{
+ reset = slider.children.first().position();
+ }
+ var animateProperty = slider.settings.mode == 'horizontal' ? -position.left : -position.top;
+ var resetValue = slider.settings.mode == 'horizontal' ? -reset.left : -reset.top;
+ var params = {resetValue: resetValue};
+ setPositionProperty(animateProperty, 'ticker', speed, params);
+ }
+
+ /**
+ * Initializes touch events
+ */
+ var initTouch = function(){
+ // initialize object to contain all touch values
+ slider.touch = {
+ start: {x: 0, y: 0},
+ end: {x: 0, y: 0}
+ }
+ slider.viewport.bind('touchstart', onTouchStart);
+ }
+
+ /**
+ * Event handler for "touchstart"
+ *
+ * @param e (event)
+ * - DOM event object
+ */
+ var onTouchStart = function(e){
+ if(slider.working){
+ e.preventDefault();
+ }else{
+ // record the original position when touch starts
+ slider.touch.originalPos = el.position();
+ var orig = e.originalEvent;
+ // record the starting touch x, y coordinates
+ slider.touch.start.x = orig.changedTouches[0].pageX;
+ slider.touch.start.y = orig.changedTouches[0].pageY;
+ // bind a "touchmove" event to the viewport
+ slider.viewport.bind('touchmove', onTouchMove);
+ // bind a "touchend" event to the viewport
+ slider.viewport.bind('touchend', onTouchEnd);
+ }
+ }
+
+ /**
+ * Event handler for "touchmove"
+ *
+ * @param e (event)
+ * - DOM event object
+ */
+ var onTouchMove = function(e){
+ var orig = e.originalEvent;
+ // if scrolling on y axis, do not prevent default
+ var xMovement = Math.abs(orig.changedTouches[0].pageX - slider.touch.start.x);
+ var yMovement = Math.abs(orig.changedTouches[0].pageY - slider.touch.start.y);
+ // x axis swipe
+ if((xMovement * 3) > yMovement && slider.settings.preventDefaultSwipeX){
+ e.preventDefault();
+ // y axis swipe
+ }else if((yMovement * 3) > xMovement && slider.settings.preventDefaultSwipeY){
+ e.preventDefault();
+ }
+ if(slider.settings.mode != 'fade' && slider.settings.oneToOneTouch){
+ var value = 0;
+ // if horizontal, drag along x axis
+ if(slider.settings.mode == 'horizontal'){
+ var change = orig.changedTouches[0].pageX - slider.touch.start.x;
+ value = slider.touch.originalPos.left + change;
+ // if vertical, drag along y axis
+ }else{
+ var change = orig.changedTouches[0].pageY - slider.touch.start.y;
+ value = slider.touch.originalPos.top + change;
+ }
+ setPositionProperty(value, 'reset', 0);
+ }
+ }
+
+ /**
+ * Event handler for "touchend"
+ *
+ * @param e (event)
+ * - DOM event object
+ */
+ var onTouchEnd = function(e){
+ slider.viewport.unbind('touchmove', onTouchMove);
+ var orig = e.originalEvent;
+ var value = 0;
+ // record end x, y positions
+ slider.touch.end.x = orig.changedTouches[0].pageX;
+ slider.touch.end.y = orig.changedTouches[0].pageY;
+ // if fade mode, check if absolute x distance clears the threshold
+ if(slider.settings.mode == 'fade'){
+ var distance = Math.abs(slider.touch.start.x - slider.touch.end.x);
+ if(distance >= slider.settings.swipeThreshold){
+ slider.touch.start.x > slider.touch.end.x ? el.goToNextSlide() : el.goToPrevSlide();
+ el.stopAuto();
+ }
+ // not fade mode
+ }else{
+ var distance = 0;
+ // calculate distance and el's animate property
+ if(slider.settings.mode == 'horizontal'){
+ distance = slider.touch.end.x - slider.touch.start.x;
+ value = slider.touch.originalPos.left;
+ }else{
+ distance = slider.touch.end.y - slider.touch.start.y;
+ value = slider.touch.originalPos.top;
+ }
+ // if not infinite loop and first / last slide, do not attempt a slide transition
+ if(!slider.settings.infiniteLoop && ((slider.active.index == 0 && distance > 0) || (slider.active.last && distance < 0))){
+ setPositionProperty(value, 'reset', 200);
+ }else{
+ // check if distance clears threshold
+ if(Math.abs(distance) >= slider.settings.swipeThreshold){
+ distance < 0 ? el.goToNextSlide() : el.goToPrevSlide();
+ el.stopAuto();
+ }else{
+ // el.animate(property, 200);
+ setPositionProperty(value, 'reset', 200);
+ }
+ }
+ }
+ slider.viewport.unbind('touchend', onTouchEnd);
+ }
+
+ /**
+ * Window resize event callback
+ */
+ var resizeWindow = function(e){
+ // don't do anything if slider isn't initialized.
+ if(!slider.initialized) return;
+ // get the new window dimens (again, thank you IE)
+ var windowWidthNew = $(window).width();
+ var windowHeightNew = $(window).height();
+ // make sure that it is a true window resize
+ // *we must check this because our dinosaur friend IE fires a window resize event when certain DOM elements
+ // are resized. Can you just die already?*
+ if(windowWidth != windowWidthNew || windowHeight != windowHeightNew){
+ // set the new window dimens
+ windowWidth = windowWidthNew;
+ windowHeight = windowHeightNew;
+ // update all dynamic elements
+ el.redrawSlider();
+ // Call user resize handler
+ slider.settings.onSliderResize.call(el, slider.active.index);
+ }
+ }
+
+ /**
+ * ===================================================================================
+ * = PUBLIC FUNCTIONS
+ * ===================================================================================
+ */
+
+ /**
+ * Performs slide transition to the specified slide
+ *
+ * @param slideIndex (int)
+ * - the destination slide's index (zero-based)
+ *
+ * @param direction (string)
+ * - INTERNAL USE ONLY - the direction of travel ("prev" / "next")
+ */
+ el.goToSlide = function(slideIndex, direction){
+ // if plugin is currently in motion, ignore request
+ if(slider.working || slider.active.index == slideIndex) return;
+ // declare that plugin is in motion
+ slider.working = true;
+ // store the old index
+ slider.oldIndex = slider.active.index;
+ // if slideIndex is less than zero, set active index to last child (this happens during infinite loop)
+ if(slideIndex < 0){
+ slider.active.index = getPagerQty() - 1;
+ // if slideIndex is greater than children length, set active index to 0 (this happens during infinite loop)
+ }else if(slideIndex >= getPagerQty()){
+ slider.active.index = 0;
+ // set active index to requested slide
+ }else{
+ slider.active.index = slideIndex;
+ }
+ // onSlideBefore, onSlideNext, onSlidePrev callbacks
+ slider.settings.onSlideBefore(slider.children.eq(slider.active.index), slider.oldIndex, slider.active.index);
+ if(direction == 'next'){
+ slider.settings.onSlideNext(slider.children.eq(slider.active.index), slider.oldIndex, slider.active.index);
+ }else if(direction == 'prev'){
+ slider.settings.onSlidePrev(slider.children.eq(slider.active.index), slider.oldIndex, slider.active.index);
+ }
+ // check if last slide
+ slider.active.last = slider.active.index >= getPagerQty() - 1;
+ // update the pager with active class
+ if(slider.settings.pager) updatePagerActive(slider.active.index);
+ // // check for direction control update
+ if(slider.settings.controls) updateDirectionControls();
+ // if slider is set to mode: "fade"
+ if(slider.settings.mode == 'fade'){
+ // if adaptiveHeight is true and next height is different from current height, animate to the new height
+ if(slider.settings.adaptiveHeight && slider.viewport.height() != getViewportHeight()){
+ slider.viewport.animate({height: getViewportHeight()}, slider.settings.adaptiveHeightSpeed);
+ }
+ // fade out the visible child and reset its z-index value
+ slider.children.filter(':visible').fadeOut(slider.settings.speed).css({zIndex: 0});
+ // fade in the newly requested slide
+ slider.children.eq(slider.active.index).css('zIndex', slider.settings.slideZIndex+1).fadeIn(slider.settings.speed, function(){
+ $(this).css('zIndex', slider.settings.slideZIndex);
+ updateAfterSlideTransition();
+ });
+ // slider mode is not "fade"
+ }else{
+ // if adaptiveHeight is true and next height is different from current height, animate to the new height
+ if(slider.settings.adaptiveHeight && slider.viewport.height() != getViewportHeight()){
+ slider.viewport.animate({height: getViewportHeight()}, slider.settings.adaptiveHeightSpeed);
+ }
+ var moveBy = 0;
+ var position = {left: 0, top: 0};
+ // if carousel and not infinite loop
+ if(!slider.settings.infiniteLoop && slider.carousel && slider.active.last){
+ if(slider.settings.mode == 'horizontal'){
+ // get the last child position
+ var lastChild = slider.children.eq(slider.children.length - 1);
+ position = lastChild.position();
+ // calculate the position of the last slide
+ moveBy = slider.viewport.width() - lastChild.outerWidth();
+ }else{
+ // get last showing index position
+ var lastShowingIndex = slider.children.length - slider.settings.minSlides;
+ position = slider.children.eq(lastShowingIndex).position();
+ }
+ // horizontal carousel, going previous while on first slide (infiniteLoop mode)
+ }else if(slider.carousel && slider.active.last && direction == 'prev'){
+ // get the last child position
+ var eq = slider.settings.moveSlides == 1 ? slider.settings.maxSlides - getMoveBy() : ((getPagerQty() - 1) * getMoveBy()) - (slider.children.length - slider.settings.maxSlides);
+ var lastChild = el.children('.bx-clone').eq(eq);
+ position = lastChild.position();
+ // if infinite loop and "Next" is clicked on the last slide
+ }else if(direction == 'next' && slider.active.index == 0){
+ // get the last clone position
+ position = el.find('> .bx-clone').eq(slider.settings.maxSlides).position();
+ slider.active.last = false;
+ // normal non-zero requests
+ }else if(slideIndex >= 0){
+ var requestEl = slideIndex * getMoveBy();
+ position = slider.children.eq(requestEl).position();
+ }
+
+ /* If the position doesn't exist
+ * (e.g. if you destroy the slider on a next click),
+ * it doesn't throw an error.
+ */
+ if ("undefined" !== typeof(position)) {
+ var value = slider.settings.mode == 'horizontal' ? -(position.left - moveBy) : -position.top;
+ // plugin values to be animated
+ setPositionProperty(value, 'slide', slider.settings.speed);
+ }
+ }
+ }
+
+ /**
+ * Transitions to the next slide in the show
+ */
+ el.goToNextSlide = function(){
+ // if infiniteLoop is false and last page is showing, disregard call
+ if (!slider.settings.infiniteLoop && slider.active.last) return;
+ var pagerIndex = parseInt(slider.active.index) + 1;
+ el.goToSlide(pagerIndex, 'next');
+ }
+
+ /**
+ * Transitions to the prev slide in the show
+ */
+ el.goToPrevSlide = function(){
+ // if infiniteLoop is false and last page is showing, disregard call
+ if (!slider.settings.infiniteLoop && slider.active.index == 0) return;
+ var pagerIndex = parseInt(slider.active.index) - 1;
+ el.goToSlide(pagerIndex, 'prev');
+ }
+
+ /**
+ * Starts the auto show
+ *
+ * @param preventControlUpdate (boolean)
+ * - if true, auto controls state will not be updated
+ */
+ el.startAuto = function(preventControlUpdate){
+ // if an interval already exists, disregard call
+ if(slider.interval) return;
+ // create an interval
+ slider.interval = setInterval(function(){
+ slider.settings.autoDirection == 'next' ? el.goToNextSlide() : el.goToPrevSlide();
+ }, slider.settings.pause);
+ // if auto controls are displayed and preventControlUpdate is not true
+ if (slider.settings.autoControls && preventControlUpdate != true) updateAutoControls('stop');
+ }
+
+ /**
+ * Stops the auto show
+ *
+ * @param preventControlUpdate (boolean)
+ * - if true, auto controls state will not be updated
+ */
+ el.stopAuto = function(preventControlUpdate){
+ // if no interval exists, disregard call
+ if(!slider.interval) return;
+ // clear the interval
+ clearInterval(slider.interval);
+ slider.interval = null;
+ // if auto controls are displayed and preventControlUpdate is not true
+ if (slider.settings.autoControls && preventControlUpdate != true) updateAutoControls('start');
+ }
+
+ /**
+ * Returns current slide index (zero-based)
+ */
+ el.getCurrentSlide = function(){
+ return slider.active.index;
+ }
+
+ /**
+ * Returns current slide element
+ */
+ el.getCurrentSlideElement = function(){
+ return slider.children.eq(slider.active.index);
+ }
+
+ /**
+ * Returns number of slides in show
+ */
+ el.getSlideCount = function(){
+ return slider.children.length;
+ }
+
+ /**
+ * Update all dynamic slider elements
+ */
+ el.redrawSlider = function(){
+ // resize all children in ratio to new screen size
+ slider.children.add(el.find('.bx-clone')).width(getSlideWidth());
+ // adjust the height
+ slider.viewport.css('height', getViewportHeight());
+ // update the slide position
+ if(!slider.settings.ticker) setSlidePosition();
+ // if active.last was true before the screen resize, we want
+ // to keep it last no matter what screen size we end on
+ if (slider.active.last) slider.active.index = getPagerQty() - 1;
+ // if the active index (page) no longer exists due to the resize, simply set the index as last
+ if (slider.active.index >= getPagerQty()) slider.active.last = true;
+ // if a pager is being displayed and a custom pager is not being used, update it
+ if(slider.settings.pager && !slider.settings.pagerCustom){
+ populatePager();
+ updatePagerActive(slider.active.index);
+ }
+ }
+
+ /**
+ * Destroy the current instance of the slider (revert everything back to original state)
+ */
+ el.destroySlider = function(){
+ // don't do anything if slider has already been destroyed
+ if(!slider.initialized) return;
+ slider.initialized = false;
+ $('.bx-clone', this).remove();
+ slider.children.each(function() {
+ $(this).data("origStyle") != undefined ? $(this).attr("style", $(this).data("origStyle")) : $(this).removeAttr('style');
+ });
+ $(this).data("origStyle") != undefined ? this.attr("style", $(this).data("origStyle")) : $(this).removeAttr('style');
+ $(this).unwrap().unwrap();
+ if(slider.controls.el) slider.controls.el.remove();
+ if(slider.controls.next) slider.controls.next.remove();
+ if(slider.controls.prev) slider.controls.prev.remove();
+ if(slider.pagerEl && slider.settings.controls) slider.pagerEl.remove();
+ $('.bx-caption', this).remove();
+ if(slider.controls.autoEl) slider.controls.autoEl.remove();
+ clearInterval(slider.interval);
+ if(slider.settings.responsive) $(window).unbind('resize', resizeWindow);
+ }
+
+ /**
+ * Reload the slider (revert all DOM changes, and re-initialize)
+ */
+ el.reloadSlider = function(settings){
+ if (settings != undefined) options = settings;
+ el.destroySlider();
+ init();
+ }
+
+ init();
+
+ // returns the current jQuery object
+ return this;
+ }
+
+})(jQuery);
diff --git a/app/assets/javascripts/jquery.easing.1.3.js b/app/assets/javascripts/jquery.easing.1.3.js
new file mode 100755
index 0000000..03b7fb6
--- /dev/null
+++ b/app/assets/javascripts/jquery.easing.1.3.js
@@ -0,0 +1,205 @@
+/*
+ * jQuery Easing v1.3 - http://gsgd.co.uk/sandbox/jquery/easing/
+ *
+ * Uses the built in easing capabilities added In jQuery 1.1
+ * to offer multiple easing options
+ *
+ * TERMS OF USE - jQuery Easing
+ *
+ * Open source under the BSD License.
+ *
+ * Copyright © 2008 George McGinley Smith
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * Neither the name of the author nor the names of contributors may be used to endorse
+ * or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+*/
+
+// t: current time, b: begInnIng value, c: change In value, d: duration
+jQuery.easing['jswing'] = jQuery.easing['swing'];
+
+jQuery.extend( jQuery.easing,
+{
+ def: 'easeOutQuad',
+ swing: function (x, t, b, c, d) {
+ //alert(jQuery.easing.default);
+ return jQuery.easing[jQuery.easing.def](x, t, b, c, d);
+ },
+ easeInQuad: function (x, t, b, c, d) {
+ return c*(t/=d)*t + b;
+ },
+ easeOutQuad: function (x, t, b, c, d) {
+ return -c *(t/=d)*(t-2) + b;
+ },
+ easeInOutQuad: function (x, t, b, c, d) {
+ if ((t/=d/2) < 1) return c/2*t*t + b;
+ return -c/2 * ((--t)*(t-2) - 1) + b;
+ },
+ easeInCubic: function (x, t, b, c, d) {
+ return c*(t/=d)*t*t + b;
+ },
+ easeOutCubic: function (x, t, b, c, d) {
+ return c*((t=t/d-1)*t*t + 1) + b;
+ },
+ easeInOutCubic: function (x, t, b, c, d) {
+ if ((t/=d/2) < 1) return c/2*t*t*t + b;
+ return c/2*((t-=2)*t*t + 2) + b;
+ },
+ easeInQuart: function (x, t, b, c, d) {
+ return c*(t/=d)*t*t*t + b;
+ },
+ easeOutQuart: function (x, t, b, c, d) {
+ return -c * ((t=t/d-1)*t*t*t - 1) + b;
+ },
+ easeInOutQuart: function (x, t, b, c, d) {
+ if ((t/=d/2) < 1) return c/2*t*t*t*t + b;
+ return -c/2 * ((t-=2)*t*t*t - 2) + b;
+ },
+ easeInQuint: function (x, t, b, c, d) {
+ return c*(t/=d)*t*t*t*t + b;
+ },
+ easeOutQuint: function (x, t, b, c, d) {
+ return c*((t=t/d-1)*t*t*t*t + 1) + b;
+ },
+ easeInOutQuint: function (x, t, b, c, d) {
+ if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b;
+ return c/2*((t-=2)*t*t*t*t + 2) + b;
+ },
+ easeInSine: function (x, t, b, c, d) {
+ return -c * Math.cos(t/d * (Math.PI/2)) + c + b;
+ },
+ easeOutSine: function (x, t, b, c, d) {
+ return c * Math.sin(t/d * (Math.PI/2)) + b;
+ },
+ easeInOutSine: function (x, t, b, c, d) {
+ return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;
+ },
+ easeInExpo: function (x, t, b, c, d) {
+ return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b;
+ },
+ easeOutExpo: function (x, t, b, c, d) {
+ return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
+ },
+ easeInOutExpo: function (x, t, b, c, d) {
+ if (t==0) return b;
+ if (t==d) return b+c;
+ if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;
+ return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
+ },
+ easeInCirc: function (x, t, b, c, d) {
+ return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b;
+ },
+ easeOutCirc: function (x, t, b, c, d) {
+ return c * Math.sqrt(1 - (t=t/d-1)*t) + b;
+ },
+ easeInOutCirc: function (x, t, b, c, d) {
+ if ((t/=d/2) < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b;
+ return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b;
+ },
+ easeInElastic: function (x, t, b, c, d) {
+ var s=1.70158;var p=0;var a=c;
+ if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3;
+ if (a < Math.abs(c)) { a=c; var s=p/4; }
+ else var s = p/(2*Math.PI) * Math.asin (c/a);
+ return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
+ },
+ easeOutElastic: function (x, t, b, c, d) {
+ var s=1.70158;var p=0;var a=c;
+ if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3;
+ if (a < Math.abs(c)) { a=c; var s=p/4; }
+ else var s = p/(2*Math.PI) * Math.asin (c/a);
+ return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b;
+ },
+ easeInOutElastic: function (x, t, b, c, d) {
+ var s=1.70158;var p=0;var a=c;
+ if (t==0) return b; if ((t/=d/2)==2) return b+c; if (!p) p=d*(.3*1.5);
+ if (a < Math.abs(c)) { a=c; var s=p/4; }
+ else var s = p/(2*Math.PI) * Math.asin (c/a);
+ if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
+ return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b;
+ },
+ easeInBack: function (x, t, b, c, d, s) {
+ if (s == undefined) s = 1.70158;
+ return c*(t/=d)*t*((s+1)*t - s) + b;
+ },
+ easeOutBack: function (x, t, b, c, d, s) {
+ if (s == undefined) s = 1.70158;
+ return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
+ },
+ easeInOutBack: function (x, t, b, c, d, s) {
+ if (s == undefined) s = 1.70158;
+ if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
+ return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
+ },
+ easeInBounce: function (x, t, b, c, d) {
+ return c - jQuery.easing.easeOutBounce (x, d-t, 0, c, d) + b;
+ },
+ easeOutBounce: function (x, t, b, c, d) {
+ if ((t/=d) < (1/2.75)) {
+ return c*(7.5625*t*t) + b;
+ } else if (t < (2/2.75)) {
+ return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b;
+ } else if (t < (2.5/2.75)) {
+ return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b;
+ } else {
+ return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b;
+ }
+ },
+ easeInOutBounce: function (x, t, b, c, d) {
+ if (t < d/2) return jQuery.easing.easeInBounce (x, t*2, 0, c, d) * .5 + b;
+ return jQuery.easing.easeOutBounce (x, t*2-d, 0, c, d) * .5 + c*.5 + b;
+ }
+});
+
+/*
+ *
+ * TERMS OF USE - EASING EQUATIONS
+ *
+ * Open source under the BSD License.
+ *
+ * Copyright © 2001 Robert Penner
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * Neither the name of the author nor the names of contributors may be used to endorse
+ * or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
\ No newline at end of file
diff --git a/app/assets/javascripts/manager.js b/app/assets/javascripts/manager.js
new file mode 100644
index 0000000..2cc2489
--- /dev/null
+++ b/app/assets/javascripts/manager.js
@@ -0,0 +1,174 @@
+var manager_response = "";
+var manager_callback_function = null;
+
+function manager_load() {
+
+
+ $(document).bind("keydown",function(e) {
+ if (e.keyCode == 27) {
+ manager_hide();
+ }
+ });
+
+
+
+}
+
+function manager_show(url){
+
+ initialize_manager();
+
+ manager_response = null;
+ $('#manager_box').html("");
+
+ $("#manager_box_place").show("fast", function (){
+ $("#manager_box_place").addClass("manager_box_place_active");
+ $('#manager_box').load(url, function (){
+ image_files_load();
+
+ }
+ )
+
+
+ });
+
+
+
+
+
+
+}
+
+
+function manager_hide(){
+ $("#manager_box_place").removeClass("manager_box_place_active");
+
+ $("body").css("overflow", "auto");
+ slider_enabled = null;
+
+
+
+}
+$(document).ready(function ($) {
+ manager_load();
+
+
+
+
+});
+
+
+
+function manager_prompt(url, callback) {
+ manager_callback_function = callback;
+ manager_show(url);
+}
+
+function manager_send_response(send_value){
+
+ manager_hide();
+ manager_callback_function(send_value);
+
+
+}
+
+function select_image_from_manager(input_id){
+
+ manager_prompt("/admin/image_files/?manager=true",function(m_return){
+
+ $('#input_'+input_id).val(m_return.image_file_id);
+ $('#img_'+input_id).attr("src",m_return.thumb);
+ $('#name_'+input_id).val(m_return.name);
+
+ });
+
+}
+
+function select_gallery_images_from_manager(gallery_content_id){
+
+ manager_prompt("/admin/image_files/?manager=true&multiple=true",function(m_return){
+
+ $.ajax({url:"/portlet/gallery_images/", type: "POST", data : { image_file_ids : m_return, gallery_content_id : gallery_content_id }});
+
+
+ });
+
+}
+
+
+
+function select_file_from_manager(input_id){
+ manager_prompt("/admin/data_files/?manager=true",function(m_return){
+
+ $('#input_'+input_id).val(m_return.data_file_id);
+
+ $('#name_'+input_id).html(m_return.name);
+
+ });
+
+}
+
+
+
+function manager_send_multiple_image_files(){
+
+
+
+ manager_send_response(multiple_selection_ids());
+}
+
+
+
+function manager_send_image_file(image_file_id){
+
+
+ var image_file_div = $('#image_file_'+image_file_id);
+
+ manager_send_response({thumb : image_file_div.attr("data_thumb"), image_file_id : image_file_div.attr("data_id"), name : image_file_div.attr("data_name")});
+}
+
+function manager_send_cible(cible_id, cible_type, cible_name){
+
+ manager_send_response({cible_id : cible_id, cible_type : cible_type, cible_name : cible_name});
+}
+
+
+function manager_send_data_file(data_file_id){
+
+
+ var data_file_div = $('#data_file_'+data_file_id);
+
+ manager_send_response({data_file_id : data_file_div.data("id"), name : data_file_div.data("name")});
+}
+
+function select_cible_from_manager(input_id){
+
+
+ manager_prompt("/admin/cibles/?manager=true",function(m_return){
+
+ $('#input_id_'+input_id).val(m_return.cible_id);
+ $('#input_type_'+input_id).val(m_return.cible_type);
+ $('#name_'+input_id).val(m_return.cible_name);
+
+ });
+
+}
+
+
+
+
+
+function initialize_manager(){
+
+ if($('#manager_box_place').length == 0){
+
+ $('body').prepend('');
+
+ }
+
+
+
+
+
+}
+
diff --git a/app/assets/javascripts/nested_fields.coffee b/app/assets/javascripts/nested_fields.coffee
new file mode 100644
index 0000000..5e1756f
--- /dev/null
+++ b/app/assets/javascripts/nested_fields.coffee
@@ -0,0 +1,11 @@
+
+@remove_fields = (link) ->
+ $(link).prev("input[type=hidden]").val "1"
+ $(link).closest(".field").hide()
+ false
+@add_fields = (link, association, content) ->
+ new_id = new Date().getTime()
+ regexp = new RegExp("new_" + association, "g")
+ $(link).closest("p").next("." + association + "_form").prepend content.replace(regexp, new_id).replace(association + "_class", "new_field")
+ $(".new_field").removeClass "new_field"
+ false
\ No newline at end of file
diff --git a/app/assets/javascripts/pane_hover.js b/app/assets/javascripts/pane_hover.js
new file mode 100644
index 0000000..b6bff46
--- /dev/null
+++ b/app/assets/javascripts/pane_hover.js
@@ -0,0 +1,55 @@
+
+
+function show_pane_hover(content, width, height, zindex){
+ var width = width || 500;
+ var height = height || 500;
+ var zindex = zindex || 1000;
+
+
+ initialize_pane_hover();
+
+
+
+
+
+ $('#qi_pane_hover_content').html(content);
+
+ $('#qi_pane_hover_content').data("height",height);
+ $('#qi_pane_hover_content').css({"width" : width+"px","height" : height+"px","z-index" : zindex, "margin-top":"-"+height+"px"});
+
+
+ $('#qi_pane_hover_content .actions').prepend('Annuler ');
+ $('#qi_pane_hover_content').css("display","block").delay(1).css({"-webkit-transition-duration":"0.5s","-moz-transition-duration":"0.5s", "margin-top":"0px"});
+
+
+
+
+}
+
+
+
+
+function close_pane_hover(){
+
+ $('#qi_pane_hover_content').css("display","block").delay(1).css({"-webkit-transition-duration":"0.5s","-moz-transition-duration":"0.5s", "margin-top":"-"+$('#qi_pane_hover_content').data("height")+"px"}).delay(500).queue(function() {
+ $('#qi_pane_hover').remove();
+ });
+
+
+
+
+
+}
+
+function initialize_pane_hover(){
+ if($('#qi_pane_hover').length == 0){
+
+ $('body').append('');
+
+ }
+
+
+}
+
+
+
diff --git a/app/assets/javascripts/public.js.coffee b/app/assets/javascripts/public.js.coffee
index 34fdb23..69f70ac 100644
--- a/app/assets/javascripts/public.js.coffee
+++ b/app/assets/javascripts/public.js.coffee
@@ -1,35 +1,316 @@
#= require jquery
-
#= require jquery_ujs
-
-#= require ./shared/jquery.easing.1.3
-#= require ./shared/jquery.fancybox-1.3.4.pack
+#= require jquery.bxslider
+#= require jquery.easing.1.3
-$ ->
+@scrollToAnchor = (aid) ->
+ aTag = $("#" + aid )
+ $("html,body").animate({scrollTop: aTag.offset().top}, 1000)
+ return
+
+
+
+bottom = 0
+prev_link = ""
+
+
+$("document").ready ->
+
+ position_img_now = ->
+
+
+
+ # alert $("#large .large-img").outerHeight(false)
+
+ imgheight = $("#large .large-img").outerHeight(false) + $("#large h3").outerHeight(false)
+
+
+ margintop = (( $(window).height() - imgheight) / 2 )
+
+ $("#large .large-img").css
+ "margin-top" :(margintop+"px")
+ #"width" : "100px"
+
+
+
+ position_img = ->
+ $("#large .large-img").one "load", ->
+
+ position_img_now()
+
+
+
+ $("#video").click ->
+ maxwidth = 1000
+ maxheight = 900
+
+
+
+
+ prev_link = $(this)
+ $("body").append "
"
+ title = false
+
+
+
+ $("#large").append "
"
+ $("#large").append " "
+ $(".img_container.first").append ''
+
+
+ $(".img_container.first").append "Vidéo de présentation "
+
+
+ $("#large").fadeIn(500)
+ position_img();
+ if $(window).height() > (maxheight+100)
+ $("#large .large-img").css
+ "max-height" : maxheight
+ else
+ $("#large .large-img").css
+ "max-height" : "85%"
+
+ if $(window).width() > (maxwidth+100)
+ $("#large .large-img").css
+ "max-width" : maxwidth
+ else
+ $("#large .large-img").css
+ "max-width" : "85%"
+ resize();
+
+ $("#large .img_container.first").css
+ "padding-top": (($(window).height()- $("#large .img_container.first iframe").height()-50)/ 2)+"px"
+ #position_img();
+
+
+ false
+
+
+ $(".expandable_image").click ->
+ maxwidth = 1000
+ maxheight = 900
+
+
+
+
+ prev_link = $(this)
+ $("body").append "
"
+ title = false
+
+
+
+ $("#large").append "
"
+ $("#large").append " "
+ $(".img_container.first").append " "
+
+ if $(this).attr "title"
+ title = $(this).attr "title"
+ $(".img_container.first").append ""+title+" "
+
+ $("#large .large-img").one "load", ->
+ $("#large").fadeIn(500)
+ position_img();
+ if $(window).height() > (maxheight+100)
+ $("#large .large-img").css
+ "max-height" : maxheight
+ else
+ $("#large .large-img").css
+ "max-height" : "85%"
+
+ if $(window).width() > (maxwidth+100)
+ $("#large .large-img").css
+ "max-width" : maxwidth
+ else
+ $("#large .large-img").css
+ "max-width" : "85%"
+
+ position_img();
+
+
+ false
+
+
+ $(".rea-gal a").click ->
+ maxwidth = 1000
+ maxheight = 900
+
+
+
+
+ prev_link = $(this)
+ $("body").append "
"
+ title = false
+
+
+ $("#large").append " "
+ $("#large").append " "
+ $("#large").append " "
+
+ $("#large").append "
"
+ $(".img_container.first").append " "
+
+ if $(this).attr "title"
+ title = $(this).attr "title"
+ $(".img_container.first").append ""+title+" "
+
+ $("#large .large-img").one "load", ->
+ $("#large").fadeIn(500)
+ position_img();
+ if $(window).height() > (maxheight+100)
+ $("#large .large-img").css
+ "max-height" : maxheight
+ else
+ $("#large .large-img").css
+ "max-height" : "85%"
+
+ if $(window).width() > (maxwidth+100)
+ $("#large .large-img").css
+ "max-width" : maxwidth
+ else
+ $("#large .large-img").css
+ "max-width" : "85%"
+
+ position_img();
+
+
+ false
+
+
+
+ $("body").on "click", "#large", ->
+ $(this).fadeOut 300, ->
+ $(this).remove()
+
+
+ $("body").on "click" ,"#large .prev",->
+
+ if prev_link.prev("a").length > 0
+ link = prev_link.closest("a").prev("a")
+
+
+
+ else
+ link = prev_link.closest("div").children("a:last")
+
+
+ #titre = photo.find("h3")
+ $(".img_container.first").fadeOut 300, ->
+ $("#large h3").remove()
+ if link.attr "title"
+ title = link.attr "title"
+ $(".img_container.first").append ""+title+" "
+
+
+ $('#large .large-img').attr("src", link.attr("href"))
+ $("#large .large-img").one "load", ->
+ $(".img_container.first").fadeIn()
+ position_img();
+ prev_link = link
+
+ false
+
+
+ $("body").on "click" ,"#large .next",->
+
+ if prev_link.next("a").length > 0
+ link = prev_link.closest("a").next("a")
+
+
+
+ else
+ link = prev_link.closest("div").children("a:first")
+
+
+ #titre = photo.find("h3")
+
+ $(".img_container.first").fadeOut 300, ->
+ $("#large h3").remove()
+ if link.attr "title"
+ title = link.attr "title"
+ $(".img_container.first").append ""+title+" "
+
+ $('#large .large-img').attr("src", link.attr("href"))
+ $("#large .large-img").one "load", ->
+ $(".img_container.first").fadeIn()
+ position_img();
+ prev_link = link
+
+ false
+
+
+ $('.gal').bxSlider
+ adaptiveHeight: true,
+ auto: true,
+ speed:1000,
+ pause:5000,
+
+
+
+
+ left =0
+
+ top = 0
+ offset= 0
+
+ resize = ->
+
+ $("iframe").each ->
+ $(this).css
+ "height" : Math.round($(this).width()/ 1.77)+"px"
+
+ if $(window).width() > 1250
+ $(".infos .main").css
+ "position" : "static"
+ $(".bottom_image").css
+ "left" : "0px"
+
+ else
+ $(".infos .main").css
+ "position" : "relative"
+ $(".bottom_image").css
+ "left" : "-120px"
+
+ $(".rea-gal").each ->
+
+ $(this).find("img").css "width", Math.floor(((100) )/ 5)+"%"
+
+
+ $("#large").css "min-height", ($(window).height()-30)+"px"
+
+ $(".bxslider").each ->
+
+
+ height = ($(window).height())
+ optimal_height = Math.round($(this).width()/ $(this).data("ratio"))
+ if optimal_height < height
+ height = optimal_height
+
+ $(this).find("li").css("max-height", height+"px")
+
+
+ position_img_now();
+
+
+
+
+
+ resize()
+ $('.bxslider').bxSlider
+ mode: 'fade'
+ captions: true
+ auto: true
+ resize()
+ $(window).on "resize", ->
+ resize()
+
+
+
+
+
+
-
-
- $("a[rel^='prettyPhoto']").fancybox()
- $("#legals").click ->
-
- $('#legals_large').toggle()
- $('#legals_large .content').css("margin-top",( ($(window).height()-$("#legals_large .content").height())/2)+"px")
- return false
-
- $("#legals_large").click ->
-
- $('#legals_large').toggle();
- return false
-
-
- $("#legals_large a").click ->
-
- $('#legals_large').toggle();
- return false
-
-
\ No newline at end of file
diff --git a/app/assets/javascripts/redactor.js b/app/assets/javascripts/redactor.js
index 41fae96..fe77a60 100755
--- a/app/assets/javascripts/redactor.js
+++ b/app/assets/javascripts/redactor.js
@@ -1,6 +1,6 @@
/*
- Redactor v10.0.5
- Updated: November 18, 2014
+ Redactor v10.0.1
+ Updated: October 6, 2014
http://imperavi.com/redactor/
@@ -94,13 +94,11 @@
// Functionality
$.Redactor = Redactor;
- $.Redactor.VERSION = '10.0.5';
- $.Redactor.modules = ['alignment', 'autosave', 'block', 'buffer', 'build', 'button',
- 'caret', 'clean', 'code', 'core', 'dropdown', 'file', 'focus',
- 'image', 'indent', 'inline', 'insert', 'keydown', 'keyup',
- 'lang', 'line', 'link', 'list', 'modal', 'observe', 'paragraphize',
- 'paste', 'placeholder', 'progress', 'selection', 'shortcuts',
- 'tabifier', 'tidy', 'toolbar', 'upload', 'utils'];
+ $.Redactor.VERSION = '10.0.1';
+ $.Redactor.modules = ['core', 'build', 'lang', 'toolbar', 'button', 'dropdown', 'code',
+ 'clean', 'tidy', 'paragraphize', 'tabifier', 'focus', 'placeholder', 'autosave', 'buffer', 'indent', 'alignment', 'paste',
+ 'keydown', 'keyup', 'shortcuts', 'line', 'list', 'block', 'inline', 'insert', 'caret', 'selection', 'observe',
+ 'link', 'image', 'file', 'modal', 'progress', 'upload', 'utils'];
$.Redactor.opts = {
@@ -425,676 +423,93 @@
}
},
- alignment: function()
+ core: function()
{
return {
- left: function()
+ getObject: function()
{
- this.alignment.set('');
+ return $.extend({}, this);
},
- right: function()
+ getEditor: function()
{
- this.alignment.set('right');
+ return this.$editor;
},
- center: function()
+ getBox: function()
{
- this.alignment.set('center');
+ return this.$box;
},
- justify: function()
+ getElement: function()
{
- this.alignment.set('justify');
+ return this.$element;
},
- set: function(type)
+ getTextarea: function()
{
- if (!this.utils.browser('msie')) this.$editor.focus();
-
- this.buffer.set();
- this.selection.save();
-
- this.alignment.blocks = this.selection.getBlocks();
- if (this.opts.linebreaks && this.alignment.blocks[0] === false)
+ return this.$textarea;
+ },
+ getToolbar: function()
+ {
+ return (this.$toolbar) ? this.$toolbar : false;
+ },
+ addEvent: function(name)
+ {
+ this.core.event = name;
+ },
+ getEvent: function()
+ {
+ return this.core.event;
+ },
+ setCallback: function(type, e, data)
+ {
+ var callback = this.opts[type + 'Callback'];
+ if ($.isFunction(callback))
{
- this.alignment.setText(type);
+ return (typeof data == 'undefined') ? callback.call(this, e) : callback.call(this, e, data);
}
else
{
- this.alignment.setBlocks(type);
- }
-
- this.selection.restore();
- this.code.sync();
- },
- setText: function(type)
- {
- var wrapper = this.selection.wrap('div');
- $(wrapper).attr('data-tagblock', 'redactor');
- $(wrapper).css('text-align', type);
- },
- setBlocks: function(type)
- {
- $.each(this.alignment.blocks, $.proxy(function(i, el)
- {
- var $el = this.utils.getAlignmentElement(el);
-
- if (!$el) return;
-
- if (type === '' && typeof($el.data('tagblock')) !== 'undefined')
- {
- $el.replaceWith($el.html());
- }
- else
- {
- $el.css('text-align', type);
- this.utils.removeEmptyAttr($el, 'style');
- }
-
-
- }, this));
- }
- };
- },
- autosave: function()
- {
- return {
- enable: function()
- {
- if (!this.opts.autosave) return;
-
- this.autosave.html = false;
- this.autosave.name = (this.opts.autosaveName) ? this.opts.autosaveName : this.$textarea.attr('name');
-
- if (!this.opts.autosaveOnChange)
- {
- this.autosaveInterval = setInterval($.proxy(this.autosave.load, this), this.opts.autosaveInterval * 1000);
+ return (typeof data == 'undefined') ? e : data;
}
},
- onChange: function()
+ destroy: function()
{
- if (!this.opts.autosaveOnChange) return;
+ this.core.setCallback('destroy');
+
+ // off events and remove data
+ this.$element.off('.redactor').removeData('redactor');
+ this.$editor.off('.redactor');
+
+ // common
+ this.$editor.removeClass('redactor-editor redactor-linebreaks redactor-placeholder');
+ this.$editor.removeAttr('contenteditable');
- this.autosave.load();
- },
- load: function()
- {
var html = this.code.get();
- if (this.autosave.html === html) return;
- if (this.utils.isEmpty(html)) return;
- $.ajax({
- url: this.opts.autosave,
- type: 'post',
- data: 'name=' + this.autosave.name + '&' + this.autosave.name + '=' + escape(encodeURIComponent(html)),
- success: $.proxy(function(data)
- {
- this.autosave.success(data, html);
-
- }, this)
- });
- },
- success: function(data, html)
- {
- var json;
- try
+ if (this.build.isTextarea())
{
- json = $.parseJSON(data);
+ this.$box.after(this.$element);
+ this.$box.remove();
+ this.$element.val(html).show();
}
- catch(e)
+ else
{
- //data has already been parsed
- json = data;
+ this.$box.after(this.$editor);
+ this.$box.remove();
+ this.$element.html(html).show();
}
- var callbackName = (typeof json.error == 'undefined') ? 'autosave' : 'autosaveError';
+ // paste box
+ if (this.$pasteBox) this.$pasteBox.remove();
- this.core.setCallback(callbackName, this.autosave.name, json);
- this.autosave.html = html;
- },
- disable: function()
- {
+ // modal
+ if (this.$modalBox) this.$modalBox.remove();
+ if (this.$modalOverlay) this.$modalOverlay.remove();
+
+ // buttons tooltip
+ $('.redactor-toolbar-tooltip').remove();
+
+ // autosave
clearInterval(this.autosaveInterval);
- }
- };
- },
- block: function()
- {
- return {
- formatting: function(name)
- {
- var type, value;
- if (typeof this.formatting[name].data != 'undefined') type = 'data';
- else if (typeof this.formatting[name].attr != 'undefined') type = 'attr';
- else if (typeof this.formatting[name].class != 'undefined') type = 'class';
-
- if (type) value = this.formatting[name][type];
-
- this.block.format(this.formatting[name].tag, type, value);
-
- },
- format: function(tag, type, value)
- {
- if (tag == 'quote') tag = 'blockquote';
-
- var formatTags = ['p', 'pre', 'blockquote', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'];
- if ($.inArray(tag, formatTags) == -1) return;
-
- this.block.isRemoveInline = (tag == 'pre' || tag.search(/h[1-6]/i) != -1);
-
- // focus
- if (!this.utils.browser('msie')) this.$editor.focus();
-
- this.block.blocks = this.selection.getBlocks();
-
- this.block.blocksSize = this.block.blocks.length;
- this.block.type = type;
- this.block.value = value;
-
- this.buffer.set();
- this.selection.save();
-
- this.block.set(tag);
-
- this.selection.restore();
- this.code.sync();
-
- },
- set: function(tag)
- {
- this.selection.get();
- this.block.containerTag = this.range.commonAncestorContainer.tagName;
-
- if (this.range.collapsed)
- {
- this.block.setCollapsed(tag);
- }
- else
- {
- this.block.setMultiple(tag);
- }
- },
- setCollapsed: function(tag)
- {
- var block = this.block.blocks[0];
- if (block === false) return;
-
- if (block.tagName == 'LI')
- {
- if (tag != 'blockquote') return;
-
- this.block.formatListToBlockquote();
- return;
- }
-
- var isContainerTable = (this.block.containerTag == 'TD' || this.block.containerTag == 'TH');
- if (isContainerTable && !this.opts.linebreaks)
- {
-
- document.execCommand('formatblock', false, '<' + tag + '>');
-
- block = this.selection.getBlock();
- this.block.toggle($(block));
-
- }
- else if (block.tagName.toLowerCase() != tag)
- {
- if (this.opts.linebreaks && tag == 'p')
- {
- $(block).prepend(' ').append(' ');
- this.utils.replaceWithContents(block);
- }
- else
- {
- var $formatted = this.utils.replaceToTag(block, tag);
-
- this.block.toggle($formatted);
-
- if (tag != 'p' && tag != 'blockquote') $formatted.find('img').remove();
- if (this.block.isRemoveInline) this.utils.removeInlineTags($formatted);
- if (tag == 'p' || this.block.headTag) $formatted.find('p').contents().unwrap();
-
- this.block.formatTableWrapping($formatted);
- }
- }
- else if (tag == 'blockquote' && block.tagName.toLowerCase() == tag)
- {
- // blockquote off
- if (this.opts.linebreaks)
- {
- $(block).prepend(' ').append(' ');
- this.utils.replaceWithContents(block);
- }
- else
- {
- var $el = this.utils.replaceToTag(block, 'p');
- this.block.toggle($el);
- }
- }
- else if (block.tagName.toLowerCase() == tag)
- {
- this.block.toggle($(block));
- }
-
- },
- setMultiple: function(tag)
- {
- var block = this.block.blocks[0];
- var isContainerTable = (this.block.containerTag == 'TD' || this.block.containerTag == 'TH');
-
- if (block !== false && this.block.blocksSize === 1)
- {
- if (block.tagName.toLowerCase() == tag && tag == 'blockquote')
- {
- // blockquote off
- if (this.opts.linebreaks)
- {
- $(block).prepend(' ').append(' ');
- this.utils.replaceWithContents(block);
- }
- else
- {
- var $el = this.utils.replaceToTag(block, 'p');
- this.block.toggle($el);
- }
- }
- else if (block.tagName == 'LI')
- {
- if (tag != 'blockquote') return;
-
- this.block.formatListToBlockquote();
- }
- else if (this.block.containerTag == 'BLOCKQUOTE')
- {
- this.block.formatBlockquote(tag);
- }
- else if (this.opts.linebreaks && ((isContainerTable) || (this.range.commonAncestorContainer != block)))
- {
- this.block.formatWrap(tag);
- }
- else
- {
- if (this.opts.linebreaks && tag == 'p')
- {
- $(block).prepend(' ').append(' ');
- this.utils.replaceWithContents(block);
- }
- else if (block.tagName === 'TD')
- {
- this.block.formatWrap(tag);
- }
- else
- {
- var $formatted = this.utils.replaceToTag(block, tag);
-
- this.block.toggle($formatted);
-
- if (this.block.isRemoveInline) this.utils.removeInlineTags($formatted);
- if (tag == 'p' || this.block.headTag) $formatted.find('p').contents().unwrap();
- }
- }
- }
- else
- {
- if (this.opts.linebreaks || tag != 'p')
- {
- if (tag == 'blockquote')
- {
- var count = 0;
- for (var i = 0; i < this.block.blocksSize; i++)
- {
- if (this.block.blocks[i].tagName == 'BLOCKQUOTE') count++;
- }
-
- // only blockquote selected
- if (count == this.block.blocksSize)
- {
- $.each(this.block.blocks, $.proxy(function(i,s)
- {
- if (this.opts.linebreaks)
- {
- $(s).prepend(' ').append(' ');
- this.utils.replaceWithContents(s);
- }
- else
- {
- this.utils.replaceToTag(s, 'p');
- }
-
- }, this));
-
- return;
- }
-
- }
-
- this.block.formatWrap(tag);
- }
- else
- {
- var classSize = 0;
- var toggleType = false;
- if (this.block.type == 'class')
- {
- toggleType = 'toggle';
- classSize = $(this.block.blocks).filter('.' + this.block.value).size();
-
- if (this.block.blocksSize == classSize) toggleType = 'toggle';
- else if (this.block.blocksSize > classSize) toggleType = 'set';
- else if (classSize === 0) toggleType = 'set';
-
- }
-
- var exceptTags = ['ul', 'ol', 'li', 'td', 'th', 'dl', 'dt', 'dd'];
- $.each(this.block.blocks, $.proxy(function(i,s)
- {
- if ($.inArray(s.tagName.toLowerCase(), exceptTags) != -1) return;
-
- var $formatted = this.utils.replaceToTag(s, tag);
-
- if (toggleType)
- {
- if (toggleType == 'toggle') this.block.toggle($formatted);
- else if (toggleType == 'remove') this.block.remove($formatted);
- else if (toggleType == 'set') this.block.setForce($formatted);
- }
- else this.block.toggle($formatted);
-
- if (tag != 'p' && tag != 'blockquote') $formatted.find('img').remove();
- if (this.block.isRemoveInline) this.utils.removeInlineTags($formatted);
- if (tag == 'p' || this.block.headTag) $formatted.find('p').contents().unwrap();
-
-
- }, this));
- }
- }
- },
- setForce: function($el)
- {
- if (this.block.type == 'class')
- {
- $el.addClass(this.block.value);
- return;
- }
- else if (this.block.type == 'attr' || this.block.type == 'data')
- {
- $el.attr(this.block.value.name, this.block.value.value);
- return;
- }
- },
- toggle: function($el)
- {
- if (this.block.type == 'class')
- {
- $el.toggleClass(this.block.value);
- return;
- }
- else if (this.block.type == 'attr' || this.block.type == 'data')
- {
- if ($el.attr(this.block.value.name) == this.block.value.value)
- {
- $el.removeAttr(this.block.value.name);
- }
- else
- {
- $el.attr(this.block.value.name, this.block.value.value);
- }
-
- return;
- }
- else
- {
- $el.removeAttr('style class');
- return;
- }
- },
- remove: function($el)
- {
- $el.removeClass(this.block.value);
- },
- formatListToBlockquote: function()
- {
- var block = $(this.block.blocks[0]).closest('ul, ol');
-
- $(block).find('ul, ol').contents().unwrap();
- $(block).find('li').append($(' ')).contents().unwrap();
-
- var $el = this.utils.replaceToTag(block, 'blockquote');
- this.block.toggle($el);
- },
- formatBlockquote: function(tag)
- {
- document.execCommand('outdent');
- document.execCommand('formatblock', false, tag);
-
- this.clean.clearUnverified();
- this.$editor.find('p:empty').remove();
-
- var formatted = this.selection.getBlock();
-
- if (tag != 'p')
- {
- $(formatted).find('img').remove();
- }
-
- if (!this.opts.linebreaks)
- {
- this.block.toggle($(formatted));
- }
-
- this.$editor.find('ul, ol, tr, blockquote, p').each($.proxy(this.utils.removeEmpty, this));
-
- if (this.opts.linebreaks && tag == 'p')
- {
- this.utils.replaceWithContents(formatted);
- }
-
- },
- formatWrap: function(tag)
- {
- if (this.block.containerTag == 'UL' || this.block.containerTag == 'OL')
- {
- if (tag == 'blockquote')
- {
- this.block.formatListToBlockquote();
- }
- else
- {
- return;
- }
- }
-
- var formatted = this.selection.wrap(tag);
- if (formatted === false) return;
-
- var $formatted = $(formatted);
-
- this.block.formatTableWrapping($formatted);
-
- var $elements = $formatted.find(this.opts.blockLevelElements.join(',') + ', td, table, thead, tbody, tfoot, th, tr');
-
- if ((this.opts.linebreaks && tag == 'p') || tag == 'pre' || tag == 'blockquote')
- {
- $elements.append(' ');
- }
-
- $elements.contents().unwrap();
-
- if (tag != 'p' && tag != 'blockquote') $formatted.find('img').remove();
-
- $.each(this.block.blocks, $.proxy(this.utils.removeEmpty, this));
-
- $formatted.append(this.selection.getMarker(2));
-
- if (!this.opts.linebreaks)
- {
- this.block.toggle($formatted);
- }
-
- this.$editor.find('ul, ol, tr, blockquote, p').each($.proxy(this.utils.removeEmpty, this));
- $formatted.find('blockquote:empty').remove();
-
- if (this.block.isRemoveInline)
- {
- this.utils.removeInlineTags($formatted);
- }
-
- if (this.opts.linebreaks && tag == 'p')
- {
- this.utils.replaceWithContents($formatted);
- }
-
- },
- formatTableWrapping: function($formatted)
- {
- if ($formatted.closest('table').size() === 0) return;
-
- if ($formatted.closest('tr').size() === 0) $formatted.wrap('');
- if ($formatted.closest('td').size() === 0 && $formatted.closest('th').size() === 0)
- {
- $formatted.wrap('');
- }
- },
- removeData: function(name, value)
- {
- var blocks = this.selection.getBlocks();
- $(blocks).removeAttr('data-' + name);
-
- this.code.sync();
- },
- setData: function(name, value)
- {
- var blocks = this.selection.getBlocks();
- $(blocks).attr('data-' + name, value);
-
- this.code.sync();
- },
- toggleData: function(name, value)
- {
- var blocks = this.selection.getBlocks();
- $.each(blocks, function()
- {
- if ($(this).attr('data-' + name))
- {
- $(this).removeAttr('data-' + name);
- }
- else
- {
- $(this).attr('data-' + name, value);
- }
- });
- },
- removeAttr: function(attr, value)
- {
- var blocks = this.selection.getBlocks();
- $(blocks).removeAttr(attr);
-
- this.code.sync();
- },
- setAttr: function(attr, value)
- {
- var blocks = this.selection.getBlocks();
- $(blocks).attr(attr, value);
-
- this.code.sync();
- },
- toggleAttr: function(attr, value)
- {
- var blocks = this.selection.getBlocks();
- $.each(blocks, function()
- {
- if ($(this).attr(name))
- {
- $(this).removeAttr(name);
- }
- else
- {
- $(this).attr(name, value);
- }
- });
- },
- removeClass: function(className)
- {
- var blocks = this.selection.getBlocks();
- $(blocks).removeClass(className);
-
- this.utils.removeEmptyAttr(blocks, 'class');
-
- this.code.sync();
- },
- setClass: function(className)
- {
- var blocks = this.selection.getBlocks();
- $(blocks).addClass(className);
-
- this.code.sync();
- },
- toggleClass: function(className)
- {
- var blocks = this.selection.getBlocks();
- $(blocks).toggleClass(className);
-
- this.code.sync();
- }
- };
- },
- buffer: function()
- {
- return {
- set: function(type)
- {
- if (typeof type == 'undefined' || type == 'undo')
- {
- this.buffer.setUndo();
- }
- else
- {
- this.buffer.setRedo();
- }
- },
- setUndo: function()
- {
- this.selection.save();
- this.opts.buffer.push(this.$editor.html());
- this.selection.restore();
- },
- setRedo: function()
- {
- this.selection.save();
- this.opts.rebuffer.push(this.$editor.html());
- this.selection.restore();
- },
- getUndo: function()
- {
- this.$editor.html(this.opts.buffer.pop());
- },
- getRedo: function()
- {
- this.$editor.html(this.opts.rebuffer.pop());
- },
- add: function()
- {
- this.opts.buffer.push(this.$editor.html());
- },
- undo: function()
- {
- if (this.opts.buffer.length === 0) return;
-
- this.buffer.set('redo');
- this.buffer.getUndo();
-
- this.selection.restore();
-
- setTimeout($.proxy(this.observe.load, this), 50);
- },
- redo: function()
- {
- if (this.opts.rebuffer.length === 0) return;
-
- this.buffer.set('undo');
- this.buffer.getRedo();
-
- this.selection.restore();
-
- setTimeout($.proxy(this.observe.load, this), 50);
}
};
},
@@ -1226,18 +641,11 @@
if (window.FormData === undefined || !e.dataTransfer) return true;
- var length = e.dataTransfer.files.length;
- if (length === 0)
- {
- this.code.sync();
- setTimeout($.proxy(this.clean.clearUnverified, this), 1);
- this.core.setCallback('drop', e);
-
- return true;
- }
+ var length = e.dataTransfer.files.length;
+ if (length === 0) return true;
else
{
- e.preventDefault();
+ e.preventDefault();
if (this.opts.dragImageUpload || this.opts.dragFileUpload)
{
@@ -1333,30 +741,24 @@
$.each(this.opts.plugins, $.proxy(function(i, s)
{
- if (typeof RedactorPlugins[s] === 'undefined') return;
-
- if ($.inArray(s, $.Redactor.modules) !== -1)
+ if (RedactorPlugins[s])
{
- $.error('Plugin name "' + s + '" matches the name of the Redactor\'s module.');
- return;
+ if (!$.isFunction(RedactorPlugins[s])) return;
+
+ this[s] = RedactorPlugins[s]();
+
+ var methods = this.getModuleMethods(this[s]);
+ var len = methods.length;
+
+ // bind methods
+ for (var z = 0; z < len; z++)
+ {
+ this[s][methods[z]] = this[s][methods[z]].bind(this);
+ }
+
+ if ($.isFunction(this[s].init)) this[s].init();
}
- if (!$.isFunction(RedactorPlugins[s])) return;
-
- this[s] = RedactorPlugins[s]();
-
- var methods = this.getModuleMethods(this[s]);
- var len = methods.length;
-
- // bind methods
- for (var z = 0; z < len; z++)
- {
- this[s][methods[z]] = this[s][methods[z]].bind(this);
- }
-
- if ($.isFunction(this[s].init)) this[s].init();
-
-
}, this));
@@ -1373,12 +775,369 @@
}
};
},
+ lang: function()
+ {
+ return {
+ load: function()
+ {
+ this.opts.curLang = this.opts.langs[this.opts.lang];
+ },
+ get: function(name)
+ {
+ return (typeof this.opts.curLang[name] != 'undefined') ? this.opts.curLang[name] : '';
+ }
+ };
+ },
+ toolbar: function()
+ {
+ return {
+ init: function()
+ {
+ return {
+ html:
+ {
+ title: this.lang.get('html'),
+ func: 'code.toggle'
+ },
+ formatting:
+ {
+ title: this.lang.get('formatting'),
+ dropdown:
+ {
+ p:
+ {
+ title: this.lang.get('paragraph'),
+ func: 'block.format'
+ },
+ blockquote:
+ {
+ title: this.lang.get('quote'),
+ func: 'block.format'
+ },
+ pre:
+ {
+ title: this.lang.get('code'),
+ func: 'block.format'
+ },
+ h1:
+ {
+ title: this.lang.get('header1'),
+ func: 'block.format'
+ },
+ h2:
+ {
+ title: this.lang.get('header2'),
+ func: 'block.format'
+ },
+ h3:
+ {
+ title: this.lang.get('header3'),
+ func: 'block.format'
+ },
+ h4:
+ {
+ title: this.lang.get('header4'),
+ func: 'block.format'
+ },
+ h5:
+ {
+ title: this.lang.get('header5'),
+ func: 'block.format'
+ }
+ }
+ },
+ bold:
+ {
+ title: this.lang.get('bold'),
+ func: 'inline.format'
+ },
+ italic:
+ {
+ title: this.lang.get('italic'),
+ func: 'inline.format'
+ },
+ deleted:
+ {
+ title: this.lang.get('deleted'),
+ func: 'inline.format'
+ },
+ underline:
+ {
+ title: this.lang.get('underline'),
+ func: 'inline.format'
+ },
+ unorderedlist:
+ {
+ title: '• ' + this.lang.get('unorderedlist'),
+ func: 'list.toggle'
+ },
+ orderedlist:
+ {
+ title: '1. ' + this.lang.get('orderedlist'),
+ func: 'list.toggle'
+ },
+ outdent:
+ {
+ title: '< ' + this.lang.get('outdent'),
+ func: 'indent.decrease'
+ },
+ indent:
+ {
+ title: '> ' + this.lang.get('indent'),
+ func: 'indent.increase'
+ },
+ image:
+ {
+ title: this.lang.get('image'),
+ func: 'image.show'
+ },
+ file:
+ {
+ title: this.lang.get('file'),
+ func: 'file.show'
+ },
+ link:
+ {
+ title: this.lang.get('link'),
+ dropdown:
+ {
+ link:
+ {
+ title: this.lang.get('link_insert'),
+ func: 'link.show'
+ },
+ unlink:
+ {
+ title: this.lang.get('unlink'),
+ func: 'link.unlink'
+ }
+ }
+ },
+ alignment:
+ {
+ title: this.lang.get('alignment'),
+ dropdown:
+ {
+ left:
+ {
+ title: this.lang.get('align_left'),
+ func: 'alignment.left'
+ },
+ center:
+ {
+ title: this.lang.get('align_center'),
+ func: 'alignment.center'
+ },
+ right:
+ {
+ title: this.lang.get('align_right'),
+ func: 'alignment.right'
+ },
+ justify:
+ {
+ title: this.lang.get('align_justify'),
+ func: 'alignment.justify'
+ }
+ }
+ },
+ horizontalrule:
+ {
+ title: this.lang.get('horizontalrule'),
+ func: 'line.insert'
+ }
+ };
+ },
+ build: function()
+ {
+ this.toolbar.hideButtons();
+ this.toolbar.hideButtonsOnMobile();
+ this.toolbar.isButtonSourceNeeded();
+
+ if (this.opts.buttons.length === 0) return;
+
+ this.$toolbar = this.toolbar.createContainer();
+
+ this.toolbar.setOverflow();
+ this.toolbar.append();
+ this.toolbar.setFormattingTags();
+ this.toolbar.loadButtons();
+ this.toolbar.setTabindex();
+ this.toolbar.setFixed();
+
+ // buttons response
+ if (this.opts.activeButtons)
+ {
+ this.$editor.on('mouseup.redactor keyup.redactor focus.redactor', $.proxy(this.observe.buttons, this));
+ }
+
+ },
+ createContainer: function()
+ {
+ return $('').addClass('redactor-toolbar').attr('id', 'redactor-toolbar-' + this.uuid);
+ },
+ setFormattingTags: function()
+ {
+ $.each(this.opts.toolbar.formatting.dropdown, $.proxy(function (i, s)
+ {
+ if ($.inArray(i, this.opts.formatting) == -1) delete this.opts.toolbar.formatting.dropdown[i];
+ }, this));
+
+ },
+ loadButtons: function()
+ {
+ $.each(this.opts.buttons, $.proxy(function(i, btnName)
+ {
+ if (!this.opts.toolbar[btnName]) return;
+
+ if (this.opts.fileUpload === false && btnName === 'file') return true;
+ if ((this.opts.imageUpload === false && this.opts.s3 === false) && btnName === 'image') return true;
+
+ var btnObject = this.opts.toolbar[btnName];
+ this.$toolbar.append($('').append(this.button.build(btnName, btnObject)));
+
+ }, this));
+ },
+ append: function()
+ {
+ if (this.opts.toolbarExternal)
+ {
+ this.$toolbar.addClass('redactor-toolbar-external');
+ $(this.opts.toolbarExternal).html(this.$toolbar);
+ }
+ else
+ {
+ this.$box.prepend(this.$toolbar);
+ }
+ },
+ setFixed: function()
+ {
+ if (this.utils.isMobile()) return;
+ if (this.opts.toolbarExternal) return;
+ if (!this.opts.toolbarFixed) return;
+
+ this.toolbar.observeScroll();
+ $(this.opts.toolbarFixedTarget).on('scroll.redactor', $.proxy(this.toolbar.observeScroll, this));
+
+ },
+ setTabindex: function()
+ {
+ this.$toolbar.find('a').attr('tabindex', '-1');
+ },
+ setOverflow: function()
+ {
+ if (this.utils.isMobile() && this.opts.toolbarOverflow)
+ {
+ this.$toolbar.addClass('redactor-toolbar-overflow');
+ }
+ },
+ isButtonSourceNeeded: function()
+ {
+ if (this.opts.buttonSource) return;
+
+ var index = this.opts.buttons.indexOf('html');
+ if (index !== -1)
+ {
+ this.opts.buttons.splice(index, 1);
+ }
+ },
+ hideButtons: function()
+ {
+ if (this.opts.buttonsHide.length === 0) return;
+
+ $.each(this.opts.buttonsHide, $.proxy(function(i, s)
+ {
+ var index = this.opts.buttons.indexOf(s);
+ this.opts.buttons.splice(index, 1);
+
+ }, this));
+ },
+ hideButtonsOnMobile: function()
+ {
+ if (!this.utils.isMobile() && this.opts.buttonsHideOnMobile.length === 0) return;
+
+ $.each(this.opts.buttonsHideOnMobile, $.proxy(function(i, s)
+ {
+ var index = this.opts.buttons.indexOf(s);
+ this.opts.buttons.splice(index, 1);
+
+ }, this));
+ },
+ observeScroll: function()
+ {
+ var scrollTop = $(this.opts.toolbarFixedTarget).scrollTop();
+ var boxTop = 1;
+
+ if (this.opts.toolbarFixedTarget === document)
+ {
+ boxTop = this.$box.offset().top;
+ }
+
+ if (scrollTop > boxTop)
+ {
+ this.toolbar.observeScrollEnable(scrollTop, boxTop);
+ }
+ else
+ {
+ this.toolbar.observeScrollDisable();
+ }
+ },
+ observeScrollEnable: function(scrollTop, boxTop)
+ {
+ var top = this.opts.toolbarFixedTopOffset + scrollTop - boxTop;
+ var left = 0;
+ var end = boxTop + this.$box.height() + 30;
+ var width = this.$box.innerWidth();
+
+ this.$toolbar.addClass('toolbar-fixed-box');
+ this.$toolbar.css({
+ position: 'absolute',
+ width: width,
+ top: top + 'px',
+ left: left
+ });
+
+ this.toolbar.setDropdownsFixed();
+ this.$toolbar.css('visibility', (scrollTop < end) ? 'visible' : 'hidden');
+ },
+ observeScrollDisable: function()
+ {
+ this.$toolbar.css({
+ position: 'relative',
+ width: 'auto',
+ top: 0,
+ left: 0,
+ visibility: 'visible'
+ });
+
+ this.toolbar.unsetDropdownsFixed();
+ this.$toolbar.removeClass('toolbar-fixed-box');
+
+ },
+ setDropdownsFixed: function()
+ {
+ var self = this;
+ $('.redactor-dropdown').each(function()
+ {
+ $(this).css({ position: 'fixed', top: self.$toolbar.innerHeight() + self.opts.toolbarFixedTopOffset });
+ });
+ },
+ unsetDropdownsFixed: function()
+ {
+ var self = this;
+ $('.redactor-dropdown').each(function()
+ {
+ var top = (self.$toolbar.innerHeight() + self.$toolbar.offset().top) + 'px';
+ $(this).css({ position: 'absolute', top: top });
+ });
+ }
+ };
+ },
button: function()
{
return {
build: function(btnName, btnObject)
{
- var $button = $(' ').attr('tabindex', '-1');
+ var $button = $(' ');
if (btnObject.func || btnObject.command || btnObject.dropdown)
{
@@ -1448,8 +1207,6 @@
},
onClick: function(e, btnName, type, callback)
{
- this.button.caretOffset = this.caret.getOffset();
-
e.preventDefault();
if (this.utils.browser('msie')) e.returnValue = false;
@@ -1485,6 +1242,7 @@
this.observe.buttons(e, btnName);
}
}
+
},
get: function(key)
{
@@ -1607,196 +1365,289 @@
}
};
},
- caret: function()
+ dropdown: function()
{
return {
- setStart: function(node)
+ build: function(name, $dropdown, dropdownObject)
{
- // inline tag
- if (!this.utils.isBlock(node))
+ if (name == 'formatting' && this.opts.formattingAdd)
{
- var space = this.utils.createSpaceElement();
+ $.each(this.opts.formattingAdd, $.proxy(function(i,s)
+ {
+ var name = s.tag;
+ if (typeof s.class != 'undefined')
+ {
+ name = name + '-' + s.class;
+ }
- $(node).prepend(space);
- this.caret.setEnd(space);
+ s.type = (this.utils.isBlockTag(s.tag)) ? 'block' : 'inline';
+ var func = (s.type == 'inline') ? 'inline.formatting' : 'block.formatting';
+
+ if (this.opts.linebreaks && s.type == 'block' && s.tag == 'p') return;
+
+ this.formatting[name] = {
+ tag: s.tag,
+ style: s.style,
+ 'class': s.class,
+ attr: s.attr,
+ data: s.data
+ };
+
+ dropdownObject[name] = {
+ func: func,
+ title: s.title
+ };
+
+ }, this));
+
+ }
+
+ $.each(dropdownObject, $.proxy(function(btnName, btnObject)
+ {
+ var $item = $('' + btnObject.title + ' ');
+ if (name == 'formatting') $item.addClass('redactor-formatting-' + btnName);
+
+ $item.on('click', $.proxy(function(e)
+ {
+ var type = 'func';
+ var callback = btnObject.func;
+ if (btnObject.command)
+ {
+ type = 'command';
+ callback = btnObject.command;
+ }
+ else if (btnObject.dropdown)
+ {
+ type = 'dropdown';
+ callback = btnObject.dropdown;
+ }
+
+ this.button.onClick(e, btnName, type, callback);
+
+ }, this));
+
+ $dropdown.append($item);
+
+ }, this));
+ },
+ show: function(e, key)
+ {
+ if (!this.opts.visual)
+ {
+ e.preventDefault();
+ return false;
+ }
+
+ var $button = this.button.get(key);
+
+ // Always re-append it to the end of so it always has the highest sub-z-index.
+ var $dropdown = $button.data('dropdown').appendTo(document.body);
+
+ if ($button.hasClass('dropact'))
+ {
+ this.dropdown.hideAll();
}
else
{
- this.caret.set(node, 0, node, 0);
- }
- },
- setEnd: function(node)
- {
- this.caret.set(node, 1, node, 1);
- },
- set: function(orgn, orgo, focn, foco)
- {
- // focus
- if (!this.utils.browser('msie')) this.$editor.focus();
+ this.dropdown.hideAll();
+ this.core.setCallback('dropdownShow', { dropdown: $dropdown, key: key, button: $button });
- orgn = orgn[0] || orgn;
- focn = focn[0] || focn;
+ this.button.setActive(key);
- if (this.utils.isBlockTag(orgn.tagName) && orgn.innerHTML === '')
- {
- orgn.innerHTML = this.opts.invisibleSpace;
- }
+ $button.addClass('dropact');
- if (orgn.tagName == 'BR' && this.opts.linebreaks === false)
- {
- var par = $(this.opts.emptyHtml)[0];
- $(orgn).replaceWith(par);
- orgn = par;
- focn = orgn;
- }
+ var keyPosition = $button.offset();
- this.selection.get();
-
- try {
- this.range.setStart(orgn, orgo);
- this.range.setEnd(focn, foco);
- }
- catch (e) {}
-
- this.selection.addRange();
- },
- setAfter: function(node)
- {
- try {
- var tag = $(node)[0].tagName;
-
- // inline tag
- if (tag != 'BR' && !this.utils.isBlock(node))
+ // fix right placement
+ var dropdownWidth = $dropdown.width();
+ if ((keyPosition.left + dropdownWidth) > $(document).width())
{
- var space = this.utils.createSpaceElement();
+ keyPosition.left -= dropdownWidth;
+ }
- $(node).after(space);
- this.caret.setEnd(space);
+ var left = keyPosition.left + 'px';
+ if (this.$toolbar.hasClass('toolbar-fixed-box'))
+ {
+ $dropdown.css({ position: 'fixed', left: left, top: this.$toolbar.innerHeight() + this.opts.toolbarFixedTopOffset }).show();
}
else
{
- if (tag != 'BR' && this.utils.browser('msie'))
- {
- this.caret.setStart($(node).next());
- }
- else
- {
- this.caret.setAfterOrBefore(node, 'after');
- }
+ var top = ($button.innerHeight() + keyPosition.top) + 'px';
+
+ $dropdown.css({ position: 'absolute', left: left, top: top }).show();
}
+
+
+ this.core.setCallback('dropdownShown', { dropdown: $dropdown, key: key, button: $button });
}
- catch (e) {
- var space = this.utils.createSpaceElement();
- $(node).after(space);
- this.caret.setEnd(space);
- }
+
+ $(document).one('click', $.proxy(this.dropdown.hide, this));
+ this.$editor.one('click', $.proxy(this.dropdown.hide, this));
+
+ $dropdown.on('mouseover', function() { $('html').css('overflow', 'hidden'); });
+ $dropdown.on('mouseout', function() { $('html').css('overflow', ''); });
+
+ e.stopPropagation();
},
- setBefore: function(node)
+ hideAll: function()
{
- // block tag
- if (this.utils.isBlock(node))
+ this.$toolbar.find('a.dropact').removeClass('redactor-act').removeClass('dropact');
+
+ $('.redactor-dropdown').hide();
+ this.core.setCallback('dropdownHide');
+ },
+ hide: function (e)
+ {
+ var $dropdown = $(e.target);
+ if (!$dropdown.hasClass('dropact'))
{
- this.caret.setEnd($(node).prev());
+ $dropdown.removeClass('dropact');
+ this.dropdown.hideAll();
+ }
+ }
+ };
+ },
+ code: function()
+ {
+ return {
+ set: function(html)
+ {
+ html = $.trim(html.toString());
+
+ // clean
+ html = this.clean.onSet(html);
+
+ this.$editor.html(html);
+ this.code.sync();
+
+ setTimeout($.proxy(this.buffer.add, this), 15);
+ if (this.start === false) this.observe.load();
+
+ },
+ get: function()
+ {
+ var code = this.$textarea.val();
+
+ // indent code
+ code = this.tabifier.get(code);
+
+ return code;
+ },
+ sync: function()
+ {
+ setTimeout($.proxy(this.code.startSync, this), 10);
+ },
+ startSync: function()
+ {
+ var html = this.$editor.html();
+
+ // is there a need to synchronize
+ if (this.code.syncCode && this.code.syncCode == html)
+ {
+ // do not sync
+ return;
+ }
+
+ // save code
+ this.code.syncCode = html;
+
+ // before clean callback
+ html = this.core.setCallback('syncBefore', html);
+
+ // clean
+ html = this.clean.onSync(html);
+
+ // set code
+ this.$textarea.val(html);
+
+ // after sync callback
+ this.core.setCallback('sync', html);
+
+ if (this.start === false)
+ {
+ this.core.setCallback('change', html);
+ }
+
+ this.start = false;
+
+ // autosave on change
+ this.autosave.onChange();
+ },
+ toggle: function()
+ {
+ if (this.opts.visual)
+ {
+ this.code.showCode();
}
else
{
- this.caret.setAfterOrBefore(node, 'before');
+ this.code.showVisual();
}
},
- setAfterOrBefore: function(node, type)
+ showCode: function()
{
- // focus
- if (!this.utils.browser('msie')) this.$editor.focus();
+ this.code.offset = this.caret.getOffset();
+ var scroll = $(window).scrollTop();
- node = node[0] || node;
+ var height = this.$editor.innerHeight();
- this.selection.get();
+ this.$editor.hide();
- if (type == 'after')
+ var html = this.$textarea.val();
+ this.modified = this.clean.removeSpaces(html);
+
+ // indent code
+ html = this.tabifier.get(html);
+
+ this.$textarea.val(html).height(height).show().focus();
+ this.$textarea.on('keydown.redactor-textarea-indenting', this.code.textareaIndenting);
+
+ $(window).scrollTop(scroll);
+
+ this.opts.visual = false;
+
+ this.button.setInactiveInCode();
+ this.button.setActive('html');
+ this.core.setCallback('source', html);
+ },
+ showVisual: function()
+ {
+ if (this.opts.visual) return;
+
+ var html = this.$textarea.hide().val();
+
+ if (this.modified !== this.clean.removeSpaces(html))
{
- try {
-
- this.range.setStartAfter(node);
- this.range.setEndAfter(node);
- }
- catch (e) {}
+ this.code.set(html);
}
- else
+
+ this.$editor.show();
+
+ if (!this.utils.isEmpty(html))
{
- try {
- this.range.setStartBefore(node);
- this.range.setEndBefore(node);
- }
- catch (e) {}
+ this.placeholder.remove();
}
+ this.caret.setOffset(this.code.offset);
- this.range.collapse(false);
- this.selection.addRange();
+ this.$textarea.off('keydown.redactor-textarea-indenting');
+
+ this.button.setActiveInVisual();
+ this.button.setInactive('html');
+
+ this.observe.load();
+ this.opts.visual = true;
},
- getOffsetOfElement: function(node)
+ textareaIndenting: function(e)
{
- node = node[0] || node;
+ if (e.keyCode !== 9) return true;
- this.selection.get();
+ var $el = this.$textarea;
+ var start = $el.get(0).selectionStart;
+ $el.val($el.val().substring(0, start) + "\t" + $el.val().substring($el.get(0).selectionEnd));
+ $el.get(0).selectionStart = $el.get(0).selectionEnd = start + 1;
- var cloned = this.range.cloneRange();
- cloned.selectNodeContents(node);
- cloned.setEnd(this.range.endContainer, this.range.endOffset);
-
- return $.trim(cloned.toString()).length;
- },
- getOffset: function()
- {
- var offset = 0;
- var sel = window.getSelection();
- if (sel.rangeCount > 0)
- {
- var range = window.getSelection().getRangeAt(0);
- var preCaretRange = range.cloneRange();
- preCaretRange.selectNodeContents(this.$editor[0]);
- preCaretRange.setEnd(range.endContainer, range.endOffset);
- offset = preCaretRange.toString().length;
- }
-
- return offset;
- },
- setOffset: function(start, end)
- {
- if (typeof end == 'undefined') end = start;
- if (!this.focus.isFocused()) this.focus.setStart();
-
- var range = document.createRange();
- var sel = document.getSelection();
- var node, offset = 0;
- var walker = document.createTreeWalker(this.$editor[0], NodeFilter.SHOW_TEXT, null, null);
-
- while (node = walker.nextNode())
- {
- offset += node.nodeValue.length;
- if (offset > start)
- {
- range.setStart(node, node.nodeValue.length + start - offset);
- start = Infinity;
- }
-
- if (offset >= end)
- {
- range.setEnd(node, node.nodeValue.length + end - offset);
- break;
- }
- }
-
- sel.removeAllRanges();
- sel.addRange(range);
- },
- setToPoint: function(start, end)
- {
- this.caret.setOffset(start, end);
- },
- getCoords: function()
- {
- return this.caret.getOffset();
+ return false;
}
};
},
@@ -1807,9 +1658,6 @@
{
html = this.clean.savePreCode(html);
- // convert script tag
- html = html.replace(/');
-
// restore form tag
html = this.clean.restoreFormTags(html);
@@ -1897,8 +1728,6 @@
html = html.replace(new RegExp('<(.*?) data-verified="redactor"(.*?[^>])>', 'gi'), '<$1$2>');
html = html.replace(new RegExp('])>', 'gi'), '');
html = html.replace(new RegExp(' ])>', 'gi'), ' ');
- html = html.replace(new RegExp(' ])>', 'gi'), ' ');
- html = html.replace(new RegExp(' ])>', 'gi'), ' ');
html = html.replace(new RegExp('(.*?) ', 'gi'), '$1');
html = html.replace(/ data-save-url="(.*?[^>])"/gi, '');
@@ -1907,10 +1736,6 @@
html = html.replace(/])>(.*?)<\/span>/gi, '');
html = html.replace(/])>(.*?)<\/span>/gi, '');
- // remove font tag
- html = html.replace(//gi, '');
- html = html.replace(/<\/font>/gi, '');
-
// tidy html
html = this.tidy.load(html);
@@ -2007,13 +1832,12 @@
html = this.clean.saveFormTags(html);
}
-
+ html = this.clean.onPasteIeFixLinks(html);
html = this.clean.onPasteWord(html);
html = this.clean.onPasteExtra(html);
html = this.clean.onPasteTidy(html, 'all');
-
// paragraphize
if (!this.clean.singleLine && this.opts.paragraphize)
{
@@ -2036,37 +1860,26 @@
// style
html = html.replace(/