')
- var $parent_div = $(this).closest('.ace-spinner').spinner(options).wrapInner("
")
+ $.fn.ace_spinner = function(options) {
+
+ //when min is negative, the input maxlength does not account for the extra minus sign
+ this.each(function() {
+ var icon_up = options.icon_up || 'icon-chevron-up'
+ var icon_down = options.icon_down || 'icon-chevron-down'
+ var on_sides = options.on_sides || false
+
+ var btn_up_class = options.btn_up_class || ''
+ var btn_down_class = options.btn_down_class || ''
+
+ var max = options.max || 999
+ max = (''+max).length
+
+ $(this).addClass('spinner-input form-control').wrap('
')
+ var $parent_div = $(this).closest('.ace-spinner').spinner(options).wrapInner("
")
- if(on_sides)
- {
- $(this).before('
\
- \
-
')
- .after('
\
- \
-
')
-
- $parent_div.addClass('touch-spinner')
- $parent_div.css('width' , (max * 20 + 40)+'px')
- }
- else {
- $(this).after('
\
- \
- \
-
')
+ if(on_sides)
+ {
+ $(this).before('
\
+ \
+
')
+ .after('
\
+ \
+
')
+
+ $parent_div.addClass('touch-spinner')
+ $parent_div.css('width' , (max * 20 + 40)+'px')
+ }
+ else {
+ $(this).after('
\
+ \
+ \
+
')
- if("ontouchend" in document || options.touch_spinner) {
- $parent_div.addClass('touch-spinner')
- $parent_div.css('width' , (max * 20 + 40)+'px')
- }
- else {
- $(this).next().addClass('btn-group-vertical');
- $parent_div.css('width' , (max * 20 + 10)+'px')
- }
- }
-
-
+ if("ontouchend" in document || options.touch_spinner) {
+ $parent_div.addClass('touch-spinner')
+ $parent_div.css('width' , (max * 20 + 40)+'px')
+ }
+ else {
+ $(this).next().addClass('btn-group-vertical');
+ $parent_div.css('width' , (max * 20 + 10)+'px')
+ }
+ }
+
+
- $(this).on('mousewheel DOMMouseScroll', function(event){
- var delta = event.originalEvent.detail < 0 || event.originalEvent.wheelDelta > 0 ? 1 : -1
- $parent_div.spinner('step', delta > 0)//accepts true or false as second param
- $parent_div.spinner('triggerChangedEvent')
- return false
- });
- var that = $(this);
- $parent_div.on('changed', function(){
- that.trigger('change')//trigger the input's change event
- });
-
- });
-
- return this;
- }
+ $(this).on('mousewheel DOMMouseScroll', function(event){
+ var delta = event.originalEvent.detail < 0 || event.originalEvent.wheelDelta > 0 ? 1 : -1
+ $parent_div.spinner('step', delta > 0)//accepts true or false as second param
+ $parent_div.spinner('triggerChangedEvent')
+ return false
+ });
+ var that = $(this);
+ $parent_div.on('changed', function(){
+ that.trigger('change')//trigger the input's change event
+ });
+
+ });
+
+ return this;
+ }
})(window.jQuery);
@@ -454,28 +454,28 @@ jQuery(function() {
(function($ , undefined) {
- $.fn.ace_wizard = function(options) {
-
- this.each(function() {
- var $this = $(this);
- $this.wizard();
+ $.fn.ace_wizard = function(options) {
+
+ this.each(function() {
+ var $this = $(this);
+ $this.wizard();
- var buttons = $this.siblings('.wizard-actions').eq(0);
- var $wizard = $this.data('wizard');
- $wizard.$prevBtn.remove();
- $wizard.$nextBtn.remove();
-
- $wizard.$prevBtn = buttons.find('.btn-prev').eq(0).on(ace.click_event, function(){
- $this.wizard('previous');
- }).attr('disabled', 'disabled');
- $wizard.$nextBtn = buttons.find('.btn-next').eq(0).on(ace.click_event, function(){
- $this.wizard('next');
- }).removeAttr('disabled');
- $wizard.nextText = $wizard.$nextBtn.text();
- });
-
- return this;
- }
+ var buttons = $this.siblings('.wizard-actions').eq(0);
+ var $wizard = $this.data('wizard');
+ $wizard.$prevBtn.remove();
+ $wizard.$nextBtn.remove();
+
+ $wizard.$prevBtn = buttons.find('.btn-prev').eq(0).on(ace.click_event, function(){
+ $this.wizard('previous');
+ }).attr('disabled', 'disabled');
+ $wizard.$nextBtn = buttons.find('.btn-next').eq(0).on(ace.click_event, function(){
+ $this.wizard('next');
+ }).removeAttr('disabled');
+ $wizard.nextText = $wizard.$nextBtn.text();
+ });
+
+ return this;
+ }
})(window.jQuery);
@@ -484,49 +484,49 @@ jQuery(function() {
(function($ , undefined) {
- $.fn.ace_colorpicker = function(options) {
-
- var settings = $.extend( {
- pull_right:false,
- caret:true
+ $.fn.ace_colorpicker = function(options) {
+
+ var settings = $.extend( {
+ pull_right:false,
+ caret:true
}, options);
-
- this.each(function() {
-
- var $that = $(this);
- var colors = '';
- var color = '';
- $(this).hide().find('option').each(function() {
- var $class = 'colorpick-btn';
- if(this.selected) {
- $class += ' selected';
- color = this.value;
- }
- colors += '
';
- }).end().on('change.ace_inner_call', function(){
- $(this).next().find('.btn-colorpicker').css('background-color', this.value);
- })
- .after('
')
- .next().find('.dropdown-menu').on(ace.click_event, function(e) {
- var a = $(e.target);
- if(!a.is('.colorpick-btn')) return false;
- a.closest('ul').find('.selected').removeClass('selected');
- a.addClass('selected');
- var color = a.data('color');
+
+ this.each(function() {
+
+ var $that = $(this);
+ var colors = '';
+ var color = '';
+ $(this).hide().find('option').each(function() {
+ var $class = 'colorpick-btn';
+ if(this.selected) {
+ $class += ' selected';
+ color = this.value;
+ }
+ colors += '
';
+ }).end().on('change.ace_inner_call', function(){
+ $(this).next().find('.btn-colorpicker').css('background-color', this.value);
+ })
+ .after('
')
+ .next().find('.dropdown-menu').on(ace.click_event, function(e) {
+ var a = $(e.target);
+ if(!a.is('.colorpick-btn')) return false;
+ a.closest('ul').find('.selected').removeClass('selected');
+ a.addClass('selected');
+ var color = a.data('color');
- $that.val(color).change();
+ $that.val(color).change();
- e.preventDefault();
- return true;//if false, dropdown won't hide!
- });
-
-
- });
- return this;
-
- }
-
-
+ e.preventDefault();
+ return true;//if false, dropdown won't hide!
+ });
+
+
+ });
+ return this;
+
+ }
+
+
})(window.jQuery);
@@ -541,38 +541,38 @@ jQuery(function() {
(function($ , undefined) {
- $.fn.ace_tree = function(options) {
- var $options = {
- 'open-icon' : 'icon-folder-open',
- 'close-icon' : 'icon-folder-close',
- 'selectable' : true,
- 'selected-icon' : 'icon-ok',
- 'unselected-icon' : 'tree-dot'
- }
-
- $options = $.extend({}, $options, options)
+ $.fn.ace_tree = function(options) {
+ var $options = {
+ 'open-icon' : 'icon-folder-open',
+ 'close-icon' : 'icon-folder-close',
+ 'selectable' : true,
+ 'selected-icon' : 'icon-ok',
+ 'unselected-icon' : 'tree-dot'
+ }
+
+ $options = $.extend({}, $options, options)
- this.each(function() {
- var $this = $(this);
- $this.html('
\
-
\
- '+($options['unselected-icon'] == null ? '' : '
')+'\
-
\
-
');
- $this.addClass($options['selectable'] == true ? 'tree-selectable' : 'tree-unselectable');
-
- $this.tree($options);
- });
+ this.each(function() {
+ var $this = $(this);
+ $this.html('
\
+
\
+ '+($options['unselected-icon'] == null ? '' : '
')+'\
+
\
+
');
+ $this.addClass($options['selectable'] == true ? 'tree-selectable' : 'tree-unselectable');
+
+ $this.tree($options);
+ });
- return this;
- }
+ return this;
+ }
})(window.jQuery);
@@ -589,308 +589,308 @@ jQuery(function() {
(function($ , undefined) {
- $.fn.ace_wysiwyg = function($options , undefined) {
- var options = $.extend( {
- speech_button:true,
- wysiwyg:{}
+ $.fn.ace_wysiwyg = function($options , undefined) {
+ var options = $.extend( {
+ speech_button:true,
+ wysiwyg:{}
}, $options);
- var color_values = [
- '#ac725e','#d06b64','#f83a22','#fa573c','#ff7537','#ffad46',
- '#42d692','#16a765','#7bd148','#b3dc6c','#fbe983','#fad165',
- '#92e1c0','#9fe1e7','#9fc6e7','#4986e7','#9a9cff','#b99aff',
- '#c2c2c2','#cabdbf','#cca6ac','#f691b2','#cd74e6','#a47ae2',
- '#444444'
- ]
+ var color_values = [
+ '#ac725e','#d06b64','#f83a22','#fa573c','#ff7537','#ffad46',
+ '#42d692','#16a765','#7bd148','#b3dc6c','#fbe983','#fad165',
+ '#92e1c0','#9fe1e7','#9fc6e7','#4986e7','#9a9cff','#b99aff',
+ '#c2c2c2','#cabdbf','#cca6ac','#f691b2','#cd74e6','#a47ae2',
+ '#444444'
+ ]
- var button_defaults =
- {
- 'font' : {
- values:['Arial', 'Courier', 'Comic Sans MS', 'Helvetica', 'Open Sans', 'Tahoma', 'Verdana'],
- icon:'icon-font',
- title:'Font'
- },
- 'fontSize' : {
- values:{5:'Huge', 3:'Normal', 1:'Small'},
- icon:'icon-text-height',
- title:'Font Size'
- },
- 'bold' : {
- icon : 'icon-bold',
- title : 'Bold (Ctrl/Cmd+B)'
- },
- 'italic' : {
- icon : 'icon-italic',
- title : 'Italic (Ctrl/Cmd+I)'
- },
- 'strikethrough' : {
- icon : 'icon-strikethrough',
- title : 'Strikethrough'
- },
- 'underline' : {
- icon : 'icon-underline',
- title : 'Underline'
- },
- 'insertunorderedlist' : {
- icon : 'icon-list-ul',
- title : 'Bullet list'
- },
- 'insertorderedlist' : {
- icon : 'icon-list-ol',
- title : 'Number list'
- },
- 'outdent' : {
- icon : 'icon-indent-left',
- title : 'Reduce indent (Shift+Tab)'
- },
- 'indent' : {
- icon : 'icon-indent-right',
- title : 'Indent (Tab)'
- },
- 'justifyleft' : {
- icon : 'icon-align-left',
- title : 'Align Left (Ctrl/Cmd+L)'
- },
- 'justifycenter' : {
- icon : 'icon-align-center',
- title : 'Center (Ctrl/Cmd+E)'
- },
- 'justifyright' : {
- icon : 'icon-align-right',
- title : 'Align Right (Ctrl/Cmd+R)'
- },
- 'justifyfull' : {
- icon : 'icon-align-justify',
- title : 'Justify (Ctrl/Cmd+J)'
- },
- 'createLink' : {
- icon : 'icon-link',
- title : 'Hyperlink',
- button_text : 'Add',
- placeholder : 'URL',
- button_class : 'btn-primary'
- },
- 'unlink' : {
- icon : 'icon-unlink',
- title : 'Remove Hyperlink'
- },
- 'insertImage' : {
- icon : 'icon-picture',
- title : 'Insert picture',
- button_text : '
Choose Image …',
- placeholder : 'Image URL',
- button_insert : 'Insert',
- button_class : 'btn-success',
- button_insert_class : 'btn-primary',
- choose_file: true //show the choose file button?
- },
- 'foreColor' : {
- values : color_values,
- title : 'Change Color'
- },
- 'backColor' : {
- values : color_values,
- title : 'Change Background Color'
- },
- 'undo' : {
- icon : 'icon-undo',
- title : 'Undo (Ctrl/Cmd+Z)'
- },
- 'redo' : {
- icon : 'icon-repeat',
- title : 'Redo (Ctrl/Cmd+Y)'
- },
- 'viewSource' : {
- icon : 'icon-code',
- title : 'View Source'
- }
- }
-
- var toolbar_buttons =
- options.toolbar ||
- [
- 'font',
- null,
- 'fontSize',
- null,
- 'bold',
- 'italic',
- 'strikethrough',
- 'underline',
- null,
- 'insertunorderedlist',
- 'insertorderedlist',
- 'outdent',
- 'indent',
- null,
- 'justifyleft',
- 'justifycenter',
- 'justifyright',
- 'justifyfull',
- null,
- 'createLink',
- 'unlink',
- null,
- 'insertImage',
- null,
- 'foreColor',
- null,
- 'undo',
- 'redo',
- null,
- 'viewSource'
- ]
+ var button_defaults =
+ {
+ 'font' : {
+ values:['Arial', 'Courier', 'Comic Sans MS', 'Helvetica', 'Open Sans', 'Tahoma', 'Verdana'],
+ icon:'icon-font',
+ title:'Font'
+ },
+ 'fontSize' : {
+ values:{5:'Huge', 3:'Normal', 1:'Small'},
+ icon:'icon-text-height',
+ title:'Font Size'
+ },
+ 'bold' : {
+ icon : 'icon-bold',
+ title : 'Bold (Ctrl/Cmd+B)'
+ },
+ 'italic' : {
+ icon : 'icon-italic',
+ title : 'Italic (Ctrl/Cmd+I)'
+ },
+ 'strikethrough' : {
+ icon : 'icon-strikethrough',
+ title : 'Strikethrough'
+ },
+ 'underline' : {
+ icon : 'icon-underline',
+ title : 'Underline'
+ },
+ 'insertunorderedlist' : {
+ icon : 'icon-list-ul',
+ title : 'Bullet list'
+ },
+ 'insertorderedlist' : {
+ icon : 'icon-list-ol',
+ title : 'Number list'
+ },
+ 'outdent' : {
+ icon : 'icon-indent-left',
+ title : 'Reduce indent (Shift+Tab)'
+ },
+ 'indent' : {
+ icon : 'icon-indent-right',
+ title : 'Indent (Tab)'
+ },
+ 'justifyleft' : {
+ icon : 'icon-align-left',
+ title : 'Align Left (Ctrl/Cmd+L)'
+ },
+ 'justifycenter' : {
+ icon : 'icon-align-center',
+ title : 'Center (Ctrl/Cmd+E)'
+ },
+ 'justifyright' : {
+ icon : 'icon-align-right',
+ title : 'Align Right (Ctrl/Cmd+R)'
+ },
+ 'justifyfull' : {
+ icon : 'icon-align-justify',
+ title : 'Justify (Ctrl/Cmd+J)'
+ },
+ 'createLink' : {
+ icon : 'icon-link',
+ title : 'Hyperlink',
+ button_text : 'Add',
+ placeholder : 'URL',
+ button_class : 'btn-primary'
+ },
+ 'unlink' : {
+ icon : 'icon-unlink',
+ title : 'Remove Hyperlink'
+ },
+ 'insertImage' : {
+ icon : 'icon-picture',
+ title : 'Insert picture',
+ button_text : '
Choose Image …',
+ placeholder : 'Image URL',
+ button_insert : 'Insert',
+ button_class : 'btn-success',
+ button_insert_class : 'btn-primary',
+ choose_file: true //show the choose file button?
+ },
+ 'foreColor' : {
+ values : color_values,
+ title : 'Change Color'
+ },
+ 'backColor' : {
+ values : color_values,
+ title : 'Change Background Color'
+ },
+ 'undo' : {
+ icon : 'icon-undo',
+ title : 'Undo (Ctrl/Cmd+Z)'
+ },
+ 'redo' : {
+ icon : 'icon-repeat',
+ title : 'Redo (Ctrl/Cmd+Y)'
+ },
+ 'viewSource' : {
+ icon : 'icon-code',
+ title : 'View Source'
+ }
+ }
+
+ var toolbar_buttons =
+ options.toolbar ||
+ [
+ 'font',
+ null,
+ 'fontSize',
+ null,
+ 'bold',
+ 'italic',
+ 'strikethrough',
+ 'underline',
+ null,
+ 'insertunorderedlist',
+ 'insertorderedlist',
+ 'outdent',
+ 'indent',
+ null,
+ 'justifyleft',
+ 'justifycenter',
+ 'justifyright',
+ 'justifyfull',
+ null,
+ 'createLink',
+ 'unlink',
+ null,
+ 'insertImage',
+ null,
+ 'foreColor',
+ null,
+ 'undo',
+ 'redo',
+ null,
+ 'viewSource'
+ ]
- this.each(function() {
- var toolbar = '
';
- //if we have a function to decide where to put the toolbar, then call that
- if(options.toolbar_place) toolbar = options.toolbar_place.call(this, toolbar);
- //otherwise put it just before our DIV
- else toolbar = $(this).before(toolbar).prev();
+ //if we have a function to decide where to put the toolbar, then call that
+ if(options.toolbar_place) toolbar = options.toolbar_place.call(this, toolbar);
+ //otherwise put it just before our DIV
+ else toolbar = $(this).before(toolbar).prev();
- toolbar.find('a[title]').tooltip({animation:false, container:'body'});
- toolbar.find('.dropdown-menu input:not([type=file])').on(ace.click_event, function() {return false})
- .on('change', function() {$(this).closest('.dropdown-menu').siblings('.dropdown-toggle').dropdown('toggle')})
- .on('keydown', function (e) {if(e.which == 27) {this.value='';$(this).change()}});
- toolbar.find('input[type=file]').prev().on(ace.click_event, function (e) {
- $(this).next().click();
- });
- toolbar.find('.wysiwyg_colorpicker').each(function() {
- $(this).ace_colorpicker({pull_right:true}).change(function(){
- $(this).nextAll('input').eq(0).val(this.value).change();
- }).next().find('.btn-colorpicker').tooltip({title: this.title, animation:false, container:'body'})
- });
-
- var speech_input;
- if (options.speech_button && 'onwebkitspeechchange' in (speech_input = document.createElement('input'))) {
- var editorOffset = $(this).offset();
- toolbar.append(speech_input);
- $(speech_input).attr({type:'text', 'data-edit':'inserttext','x-webkit-speech':''}).addClass('wysiwyg-speech-input')
- .css({'position':'absolute'}).offset({top: editorOffset.top, left: editorOffset.left+$(this).innerWidth()-35});
- } else speech_input = null
-
-
- //view source
- var self = $(this);
- var view_source = false;
- toolbar.find('a[data-view=source]').on('click', function(e){
- e.preventDefault();
-
- if(!view_source) {
- $('
')
- .css({'width':self.outerWidth(), 'height':self.outerHeight()})
- .val(self.html())
- .insertAfter(self)
- self.hide();
-
- $(this).addClass('active');
- }
- else {
- var textarea = self.next();
- self.html(textarea.val()).show();
- textarea.remove();
-
- $(this).removeClass('active');
- }
-
- view_source = !view_source;
- });
+ toolbar.find('a[title]').tooltip({animation:false, container:'body'});
+ toolbar.find('.dropdown-menu input:not([type=file])').on(ace.click_event, function() {return false})
+ .on('change', function() {$(this).closest('.dropdown-menu').siblings('.dropdown-toggle').dropdown('toggle')})
+ .on('keydown', function (e) {if(e.which == 27) {this.value='';$(this).change()}});
+ toolbar.find('input[type=file]').prev().on(ace.click_event, function (e) {
+ $(this).next().click();
+ });
+ toolbar.find('.wysiwyg_colorpicker').each(function() {
+ $(this).ace_colorpicker({pull_right:true}).change(function(){
+ $(this).nextAll('input').eq(0).val(this.value).change();
+ }).next().find('.btn-colorpicker').tooltip({title: this.title, animation:false, container:'body'})
+ });
+
+ var speech_input;
+ if (options.speech_button && 'onwebkitspeechchange' in (speech_input = document.createElement('input'))) {
+ var editorOffset = $(this).offset();
+ toolbar.append(speech_input);
+ $(speech_input).attr({type:'text', 'data-edit':'inserttext','x-webkit-speech':''}).addClass('wysiwyg-speech-input')
+ .css({'position':'absolute'}).offset({top: editorOffset.top, left: editorOffset.left+$(this).innerWidth()-35});
+ } else speech_input = null
+
+
+ //view source
+ var self = $(this);
+ var view_source = false;
+ toolbar.find('a[data-view=source]').on('click', function(e){
+ e.preventDefault();
+
+ if(!view_source) {
+ $('
')
+ .css({'width':self.outerWidth(), 'height':self.outerHeight()})
+ .val(self.html())
+ .insertAfter(self)
+ self.hide();
+
+ $(this).addClass('active');
+ }
+ else {
+ var textarea = self.next();
+ self.html(textarea.val()).show();
+ textarea.remove();
+
+ $(this).removeClass('active');
+ }
+
+ view_source = !view_source;
+ });
- var $options = $.extend({}, { activeToolbarClass: 'active' , toolbarSelector : toolbar }, options.wysiwyg || {})
- $(this).wysiwyg( $options );
- });
+ var $options = $.extend({}, { activeToolbarClass: 'active' , toolbarSelector : toolbar }, options.wysiwyg || {})
+ $(this).wysiwyg( $options );
+ });
- return this;
- }
+ return this;
+ }
})(window.jQuery);
\ No newline at end of file
diff --git a/static/js/lib/ace-extra.js b/static/js/lib/ace-extra.js
index 3f47d1c..ce37ce1 100644
--- a/static/js/lib/ace-extra.js
+++ b/static/js/lib/ace-extra.js
@@ -6,169 +6,169 @@ ace.config = {
}
ace.settings = {
- is : function(item, status) {
- //such as ace.settings.is('navbar', 'fixed')
- return (ace.data.get('settings', item+'-'+status) == 1)
- },
- exists : function(item, status) {
- return (ace.data.get('settings', item+'-'+status) !== null)
- },
- set : function(item, status) {
- ace.data.set('settings', item+'-'+status, 1)
- },
- unset : function(item, status) {
- ace.data.set('settings', item+'-'+status, -1)
- },
- remove : function(item, status) {
- ace.data.remove('settings', item+'-'+status)
- },
+ is : function(item, status) {
+ //such as ace.settings.is('navbar', 'fixed')
+ return (ace.data.get('settings', item+'-'+status) == 1)
+ },
+ exists : function(item, status) {
+ return (ace.data.get('settings', item+'-'+status) !== null)
+ },
+ set : function(item, status) {
+ ace.data.set('settings', item+'-'+status, 1)
+ },
+ unset : function(item, status) {
+ ace.data.set('settings', item+'-'+status, -1)
+ },
+ remove : function(item, status) {
+ ace.data.remove('settings', item+'-'+status)
+ },
- navbar_fixed : function(fix) {
- fix = fix || false;
- if(!fix && ace.settings.is('sidebar', 'fixed')) {
- ace.settings.sidebar_fixed(false);
- }
-
- var navbar = document.getElementById('navbar');
- if(fix) {
- if(!ace.hasClass(navbar , 'navbar-fixed-top')) ace.addClass(navbar , 'navbar-fixed-top');
- if(!ace.hasClass(document.body , 'navbar-fixed')) ace.addClass(document.body , 'navbar-fixed');
-
- ace.settings.set('navbar', 'fixed');
- } else {
- ace.removeClass(navbar , 'navbar-fixed-top');
- ace.removeClass(document.body , 'navbar-fixed');
-
- ace.settings.unset('navbar', 'fixed');
- }
-
- document.getElementById('ace-settings-navbar').checked = fix;
- },
+ navbar_fixed : function(fix) {
+ fix = fix || false;
+ if(!fix && ace.settings.is('sidebar', 'fixed')) {
+ ace.settings.sidebar_fixed(false);
+ }
+
+ var navbar = document.getElementById('navbar');
+ if(fix) {
+ if(!ace.hasClass(navbar , 'navbar-fixed-top')) ace.addClass(navbar , 'navbar-fixed-top');
+ if(!ace.hasClass(document.body , 'navbar-fixed')) ace.addClass(document.body , 'navbar-fixed');
+
+ ace.settings.set('navbar', 'fixed');
+ } else {
+ ace.removeClass(navbar , 'navbar-fixed-top');
+ ace.removeClass(document.body , 'navbar-fixed');
+
+ ace.settings.unset('navbar', 'fixed');
+ }
+
+ document.getElementById('ace-settings-navbar').checked = fix;
+ },
- breadcrumbs_fixed : function(fix) {
- fix = fix || false;
- if(fix && !ace.settings.is('sidebar', 'fixed')) {
- ace.settings.sidebar_fixed(true);
- }
+ breadcrumbs_fixed : function(fix) {
+ fix = fix || false;
+ if(fix && !ace.settings.is('sidebar', 'fixed')) {
+ ace.settings.sidebar_fixed(true);
+ }
- var breadcrumbs = document.getElementById('breadcrumbs');
- if(fix) {
- if(!ace.hasClass(breadcrumbs , 'breadcrumbs-fixed')) ace.addClass(breadcrumbs , 'breadcrumbs-fixed');
- if(!ace.hasClass(document.body , 'breadcrumbs-fixed')) ace.addClass(document.body , 'breadcrumbs-fixed');
-
- ace.settings.set('breadcrumbs', 'fixed');
- } else {
- ace.removeClass(breadcrumbs , 'breadcrumbs-fixed');
- ace.removeClass(document.body , 'breadcrumbs-fixed');
-
- ace.settings.unset('breadcrumbs', 'fixed');
- }
- document.getElementById('ace-settings-breadcrumbs').checked = fix;
- },
+ var breadcrumbs = document.getElementById('breadcrumbs');
+ if(fix) {
+ if(!ace.hasClass(breadcrumbs , 'breadcrumbs-fixed')) ace.addClass(breadcrumbs , 'breadcrumbs-fixed');
+ if(!ace.hasClass(document.body , 'breadcrumbs-fixed')) ace.addClass(document.body , 'breadcrumbs-fixed');
+
+ ace.settings.set('breadcrumbs', 'fixed');
+ } else {
+ ace.removeClass(breadcrumbs , 'breadcrumbs-fixed');
+ ace.removeClass(document.body , 'breadcrumbs-fixed');
+
+ ace.settings.unset('breadcrumbs', 'fixed');
+ }
+ document.getElementById('ace-settings-breadcrumbs').checked = fix;
+ },
- sidebar_fixed : function(fix) {
- fix = fix || false;
- if(!fix && ace.settings.is('breadcrumbs', 'fixed')) {
- ace.settings.breadcrumbs_fixed(false);
- }
+ sidebar_fixed : function(fix) {
+ fix = fix || false;
+ if(!fix && ace.settings.is('breadcrumbs', 'fixed')) {
+ ace.settings.breadcrumbs_fixed(false);
+ }
- if( fix && !ace.settings.is('navbar', 'fixed') ) {
- ace.settings.navbar_fixed(true);
- }
+ if( fix && !ace.settings.is('navbar', 'fixed') ) {
+ ace.settings.navbar_fixed(true);
+ }
- var sidebar = document.getElementById('sidebar');
- if(fix) {
- if( !ace.hasClass(sidebar , 'sidebar-fixed') ) ace.addClass(sidebar , 'sidebar-fixed');
- ace.settings.set('sidebar', 'fixed');
- } else {
- ace.removeClass(sidebar , 'sidebar-fixed');
- ace.settings.unset('sidebar', 'fixed');
- }
- document.getElementById('ace-settings-sidebar').checked = fix;
- },
+ var sidebar = document.getElementById('sidebar');
+ if(fix) {
+ if( !ace.hasClass(sidebar , 'sidebar-fixed') ) ace.addClass(sidebar , 'sidebar-fixed');
+ ace.settings.set('sidebar', 'fixed');
+ } else {
+ ace.removeClass(sidebar , 'sidebar-fixed');
+ ace.settings.unset('sidebar', 'fixed');
+ }
+ document.getElementById('ace-settings-sidebar').checked = fix;
+ },
- main_container_fixed : function(inside) {
- inside = inside || false;
+ main_container_fixed : function(inside) {
+ inside = inside || false;
- var main_container = document.getElementById('main-container');
- var navbar_container = document.getElementById('navbar-container');
- if(inside) {
- if( !ace.hasClass(main_container , 'container') ) ace.addClass(main_container , 'container');
- if( !ace.hasClass(navbar_container , 'container') ) ace.addClass(navbar_container , 'container');
- ace.settings.set('main-container', 'fixed');
- } else {
- ace.removeClass(main_container , 'container');
- ace.removeClass(navbar_container , 'container');
- ace.settings.unset('main-container', 'fixed');
- }
- document.getElementById('ace-settings-add-container').checked = inside;
-
-
- if(navigator.userAgent.match(/webkit/i)) {
- //webkit has a problem redrawing and moving around the sidebar background in realtime
- //so we do this, to force redraw
- //there will be no problems with webkit if the ".container" class is statically put inside HTML code.
- var sidebar = document.getElementById('sidebar')
- ace.toggleClass(sidebar , 'menu-min')
- setTimeout(function() { ace.toggleClass(sidebar , 'menu-min') } , 0)
- }
- },
+ var main_container = document.getElementById('main-container');
+ var navbar_container = document.getElementById('navbar-container');
+ if(inside) {
+ if( !ace.hasClass(main_container , 'container') ) ace.addClass(main_container , 'container');
+ if( !ace.hasClass(navbar_container , 'container') ) ace.addClass(navbar_container , 'container');
+ ace.settings.set('main-container', 'fixed');
+ } else {
+ ace.removeClass(main_container , 'container');
+ ace.removeClass(navbar_container , 'container');
+ ace.settings.unset('main-container', 'fixed');
+ }
+ document.getElementById('ace-settings-add-container').checked = inside;
+
+
+ if(navigator.userAgent.match(/webkit/i)) {
+ //webkit has a problem redrawing and moving around the sidebar background in realtime
+ //so we do this, to force redraw
+ //there will be no problems with webkit if the ".container" class is statically put inside HTML code.
+ var sidebar = document.getElementById('sidebar')
+ ace.toggleClass(sidebar , 'menu-min')
+ setTimeout(function() { ace.toggleClass(sidebar , 'menu-min') } , 0)
+ }
+ },
- sidebar_collapsed : function(collpase) {
- collpase = collpase || false;
+ sidebar_collapsed : function(collpase) {
+ collpase = collpase || false;
- var sidebar = document.getElementById('sidebar');
- var icon = document.getElementById('sidebar-collapse').querySelector('[class*="icon-"]');
- var $icon1 = icon.getAttribute('data-icon1');//the icon for expanded state
- var $icon2 = icon.getAttribute('data-icon2');//the icon for collapsed state
+ var sidebar = document.getElementById('sidebar');
+ var icon = document.getElementById('sidebar-collapse').querySelector('[class*="icon-"]');
+ var $icon1 = icon.getAttribute('data-icon1');//the icon for expanded state
+ var $icon2 = icon.getAttribute('data-icon2');//the icon for collapsed state
- if(collpase) {
- ace.addClass(sidebar , 'menu-min');
- ace.removeClass(icon , $icon1);
- ace.addClass(icon , $icon2);
+ if(collpase) {
+ ace.addClass(sidebar , 'menu-min');
+ ace.removeClass(icon , $icon1);
+ ace.addClass(icon , $icon2);
- ace.settings.set('sidebar', 'collapsed');
- } else {
- ace.removeClass(sidebar , 'menu-min');
- ace.removeClass(icon , $icon2);
- ace.addClass(icon , $icon1);
+ ace.settings.set('sidebar', 'collapsed');
+ } else {
+ ace.removeClass(sidebar , 'menu-min');
+ ace.removeClass(icon , $icon2);
+ ace.addClass(icon , $icon1);
- ace.settings.unset('sidebar', 'collapsed');
- }
+ ace.settings.unset('sidebar', 'collapsed');
+ }
- },
- /**
- select_skin : function(skin) {
- }
- */
+ },
+ /**
+ select_skin : function(skin) {
+ }
+ */
}
//check the status of something
ace.settings.check = function(item, val) {
- if(! ace.settings.exists(item, val) ) return;//no such setting specified
- var status = ace.settings.is(item, val);//is breadcrumbs-fixed? or is sidebar-collapsed? etc
-
- var mustHaveClass = {
- 'navbar-fixed' : 'navbar-fixed-top',
- 'sidebar-fixed' : 'sidebar-fixed',
- 'breadcrumbs-fixed' : 'breadcrumbs-fixed',
- 'sidebar-collapsed' : 'menu-min',
- 'main-container-fixed' : 'container'
- }
+ if(! ace.settings.exists(item, val) ) return;//no such setting specified
+ var status = ace.settings.is(item, val);//is breadcrumbs-fixed? or is sidebar-collapsed? etc
+
+ var mustHaveClass = {
+ 'navbar-fixed' : 'navbar-fixed-top',
+ 'sidebar-fixed' : 'sidebar-fixed',
+ 'breadcrumbs-fixed' : 'breadcrumbs-fixed',
+ 'sidebar-collapsed' : 'menu-min',
+ 'main-container-fixed' : 'container'
+ }
- //if an element doesn't have a specified class, but saved settings say it should, then add it
- //for example, sidebar isn't .fixed, but user fixed it on a previous page
- //or if an element has a specified class, but saved settings say it shouldn't, then remove it
- //for example, sidebar by default is minimized (.menu-min hard coded), but user expanded it and now shouldn't have 'menu-min' class
-
- var target = document.getElementById(item);//#navbar, #sidebar, #breadcrumbs
- if(status != ace.hasClass(target , mustHaveClass[item+'-'+val])) {
- ace.settings[item.replace('-','_')+'_'+val](status);//call the relevant function to mage the changes
- }
+ //if an element doesn't have a specified class, but saved settings say it should, then add it
+ //for example, sidebar isn't .fixed, but user fixed it on a previous page
+ //or if an element has a specified class, but saved settings say it shouldn't, then remove it
+ //for example, sidebar by default is minimized (.menu-min hard coded), but user expanded it and now shouldn't have 'menu-min' class
+
+ var target = document.getElementById(item);//#navbar, #sidebar, #breadcrumbs
+ if(status != ace.hasClass(target , mustHaveClass[item+'-'+val])) {
+ ace.settings[item.replace('-','_')+'_'+val](status);//call the relevant function to mage the changes
+ }
}
@@ -181,93 +181,93 @@ ace.settings.check = function(item, val) {
//method == 2, use cookies
//method not specified, use localStorage if available, otherwise cookies
ace.data_storage = function(method, undefined) {
- var prefix = 'ace.';
+ var prefix = 'ace.';
- var storage = null;
- var type = 0;
-
- if((method == 1 || method === undefined) && 'localStorage' in window && window['localStorage'] !== null) {
- storage = ace.storage;
- type = 1;
- }
- else if(storage == null && (method == 2 || method === undefined) && 'cookie' in document && document['cookie'] !== null) {
- storage = ace.cookie;
- type = 2;
- }
+ var storage = null;
+ var type = 0;
+
+ if((method == 1 || method === undefined) && 'localStorage' in window && window['localStorage'] !== null) {
+ storage = ace.storage;
+ type = 1;
+ }
+ else if(storage == null && (method == 2 || method === undefined) && 'cookie' in document && document['cookie'] !== null) {
+ storage = ace.cookie;
+ type = 2;
+ }
- //var data = {}
- this.set = function(namespace, key, value, undefined) {
- if(!storage) return;
-
- if(value === undefined) {//no namespace here?
- value = key;
- key = namespace;
+ //var data = {}
+ this.set = function(namespace, key, value, undefined) {
+ if(!storage) return;
+
+ if(value === undefined) {//no namespace here?
+ value = key;
+ key = namespace;
- if(value == null) storage.remove(prefix+key)
- else {
- if(type == 1)
- storage.set(prefix+key, value)
- else if(type == 2)
- storage.set(prefix+key, value, ace.config.cookie_expiry)
- }
- }
- else {
- if(type == 1) {//localStorage
- if(value == null) storage.remove(prefix+namespace+'.'+key)
- else storage.set(prefix+namespace+'.'+key, value);
- }
- else if(type == 2) {//cookie
- var val = storage.get(prefix+namespace);
- var tmp = val ? JSON.parse(val) : {};
+ if(value == null) storage.remove(prefix+key)
+ else {
+ if(type == 1)
+ storage.set(prefix+key, value)
+ else if(type == 2)
+ storage.set(prefix+key, value, ace.config.cookie_expiry)
+ }
+ }
+ else {
+ if(type == 1) {//localStorage
+ if(value == null) storage.remove(prefix+namespace+'.'+key)
+ else storage.set(prefix+namespace+'.'+key, value);
+ }
+ else if(type == 2) {//cookie
+ var val = storage.get(prefix+namespace);
+ var tmp = val ? JSON.parse(val) : {};
- if(value == null) {
- delete tmp[key];//remove
- if(ace.sizeof(tmp) == 0) {//no other elements in this cookie, so delete it
- storage.remove(prefix+namespace);
- return;
- }
- }
-
- else {
- tmp[key] = value;
- }
+ if(value == null) {
+ delete tmp[key];//remove
+ if(ace.sizeof(tmp) == 0) {//no other elements in this cookie, so delete it
+ storage.remove(prefix+namespace);
+ return;
+ }
+ }
+
+ else {
+ tmp[key] = value;
+ }
- storage.set(prefix+namespace , JSON.stringify(tmp), ace.config.cookie_expiry)
- }
- }
- }
+ storage.set(prefix+namespace , JSON.stringify(tmp), ace.config.cookie_expiry)
+ }
+ }
+ }
- this.get = function(namespace, key, undefined) {
- if(!storage) return null;
-
- if(key === undefined) {//no namespace here?
- key = namespace;
- return storage.get(prefix+key);
- }
- else {
- if(type == 1) {//localStorage
- return storage.get(prefix+namespace+'.'+key);
- }
- else if(type == 2) {//cookie
- var val = storage.get(prefix+namespace);
- var tmp = val ? JSON.parse(val) : {};
- return key in tmp ? tmp[key] : null;
- }
- }
- }
+ this.get = function(namespace, key, undefined) {
+ if(!storage) return null;
+
+ if(key === undefined) {//no namespace here?
+ key = namespace;
+ return storage.get(prefix+key);
+ }
+ else {
+ if(type == 1) {//localStorage
+ return storage.get(prefix+namespace+'.'+key);
+ }
+ else if(type == 2) {//cookie
+ var val = storage.get(prefix+namespace);
+ var tmp = val ? JSON.parse(val) : {};
+ return key in tmp ? tmp[key] : null;
+ }
+ }
+ }
-
- this.remove = function(namespace, key, undefined) {
- if(!storage) return;
-
- if(key === undefined) {
- key = namespace
- this.set(key, null);
- }
- else {
- this.set(namespace, key, null);
- }
- }
+
+ this.remove = function(namespace, key, undefined) {
+ if(!storage) return;
+
+ if(key === undefined) {
+ key = namespace
+ this.set(key, null);
+ }
+ else {
+ this.set(namespace, key, null);
+ }
+ }
}
@@ -276,84 +276,84 @@ ace.data_storage = function(method, undefined) {
//cookie storage
ace.cookie = {
- // The following functions are from Cookie.js class in TinyMCE, Moxiecode, used under LGPL.
+ // The following functions are from Cookie.js class in TinyMCE, Moxiecode, used under LGPL.
- /**
- * Get a cookie.
- */
- get : function(name) {
- var cookie = document.cookie, e, p = name + "=", b;
+ /**
+ * Get a cookie.
+ */
+ get : function(name) {
+ var cookie = document.cookie, e, p = name + "=", b;
- if ( !cookie )
- return;
+ if ( !cookie )
+ return;
- b = cookie.indexOf("; " + p);
+ b = cookie.indexOf("; " + p);
- if ( b == -1 ) {
- b = cookie.indexOf(p);
+ if ( b == -1 ) {
+ b = cookie.indexOf(p);
- if ( b != 0 )
- return null;
+ if ( b != 0 )
+ return null;
- } else {
- b += 2;
- }
+ } else {
+ b += 2;
+ }
- e = cookie.indexOf(";", b);
+ e = cookie.indexOf(";", b);
- if ( e == -1 )
- e = cookie.length;
+ if ( e == -1 )
+ e = cookie.length;
- return decodeURIComponent( cookie.substring(b + p.length, e) );
- },
+ return decodeURIComponent( cookie.substring(b + p.length, e) );
+ },
- /**
- * Set a cookie.
- *
- * The 'expires' arg can be either a JS Date() object set to the expiration date (back-compat)
- * or the number of seconds until expiration
- */
- set : function(name, value, expires, path, domain, secure) {
- var d = new Date();
+ /**
+ * Set a cookie.
+ *
+ * The 'expires' arg can be either a JS Date() object set to the expiration date (back-compat)
+ * or the number of seconds until expiration
+ */
+ set : function(name, value, expires, path, domain, secure) {
+ var d = new Date();
- if ( typeof(expires) == 'object' && expires.toGMTString ) {
- expires = expires.toGMTString();
- } else if ( parseInt(expires, 10) ) {
- d.setTime( d.getTime() + ( parseInt(expires, 10) * 1000 ) ); // time must be in miliseconds
- expires = d.toGMTString();
- } else {
- expires = '';
- }
+ if ( typeof(expires) == 'object' && expires.toGMTString ) {
+ expires = expires.toGMTString();
+ } else if ( parseInt(expires, 10) ) {
+ d.setTime( d.getTime() + ( parseInt(expires, 10) * 1000 ) ); // time must be in miliseconds
+ expires = d.toGMTString();
+ } else {
+ expires = '';
+ }
- document.cookie = name + "=" + encodeURIComponent(value) +
- ((expires) ? "; expires=" + expires : "") +
- ((path) ? "; path=" + path : "") +
- ((domain) ? "; domain=" + domain : "") +
- ((secure) ? "; secure" : "");
- },
+ document.cookie = name + "=" + encodeURIComponent(value) +
+ ((expires) ? "; expires=" + expires : "") +
+ ((path) ? "; path=" + path : "") +
+ ((domain) ? "; domain=" + domain : "") +
+ ((secure) ? "; secure" : "");
+ },
- /**
- * Remove a cookie.
- *
- * This is done by setting it to an empty value and setting the expiration time in the past.
- */
- remove : function(name, path) {
- this.set(name, '', -1000, path);
- }
+ /**
+ * Remove a cookie.
+ *
+ * This is done by setting it to an empty value and setting the expiration time in the past.
+ */
+ remove : function(name, path) {
+ this.set(name, '', -1000, path);
+ }
};
//local storage
ace.storage = {
- get: function(key) {
- return window['localStorage'].getItem(key);
- },
- set: function(key, value) {
- window['localStorage'].setItem(key , value);
- },
- remove: function(key) {
- window['localStorage'].removeItem(key);
- }
+ get: function(key) {
+ return window['localStorage'].getItem(key);
+ },
+ set: function(key, value) {
+ window['localStorage'].setItem(key , value);
+ },
+ remove: function(key) {
+ window['localStorage'].removeItem(key);
+ }
};
@@ -364,34 +364,34 @@ ace.storage = {
//count the number of properties in an object
//useful for getting the number of elements in an associative array
ace.sizeof = function(obj) {
- var size = 0;
- for(var key in obj) if(obj.hasOwnProperty(key)) size++;
- return size;
+ var size = 0;
+ for(var key in obj) if(obj.hasOwnProperty(key)) size++;
+ return size;
}
//because jQuery may not be loaded at this stage, we use our own toggleClass
ace.hasClass = function(elem, className) {
- return (" " + elem.className + " ").indexOf(" " + className + " ") > -1;
+ return (" " + elem.className + " ").indexOf(" " + className + " ") > -1;
}
ace.addClass = function(elem, className) {
if (!ace.hasClass(elem, className)) {
- var currentClass = elem.className;
- elem.className = currentClass + (currentClass.length? " " : "") + className;
+ var currentClass = elem.className;
+ elem.className = currentClass + (currentClass.length? " " : "") + className;
}
}
ace.removeClass = function(elem, className) {ace.replaceClass(elem, className);}
ace.replaceClass = function(elem, className, newClass) {
- var classToRemove = new RegExp(("(^|\\s)" + className + "(\\s|$)"), "i");
- elem.className = elem.className.replace(classToRemove, function (match, p1, p2) {
- return newClass? (p1 + newClass + p2) : " ";
- }).replace(/^\s+|\s+$/g, "");
+ var classToRemove = new RegExp(("(^|\\s)" + className + "(\\s|$)"), "i");
+ elem.className = elem.className.replace(classToRemove, function (match, p1, p2) {
+ return newClass? (p1 + newClass + p2) : " ";
+ }).replace(/^\s+|\s+$/g, "");
}
ace.toggleClass = function(elem, className) {
- if(ace.hasClass(elem, className))
- ace.removeClass(elem, className);
- else ace.addClass(elem, className);
+ if(ace.hasClass(elem, className))
+ ace.removeClass(elem, className);
+ else ace.addClass(elem, className);
}
diff --git a/static/js/lib/ace.js b/static/js/lib/ace.js
index 7d5d1c1..174e075 100644
--- a/static/js/lib/ace.js
+++ b/static/js/lib/ace.js
@@ -1,85 +1,85 @@
if(! ('ace' in window) ) window['ace'] = {}
jQuery(function($) {
- //at some places we try to use 'tap' event instead of 'click' if jquery mobile plugin is available
- window['ace'].click_event = $.fn.tap ? "tap" : "click";
+ //at some places we try to use 'tap' event instead of 'click' if jquery mobile plugin is available
+ window['ace'].click_event = $.fn.tap ? "tap" : "click";
});
ace.handle_side_menu = function($) {
- $('#menu-toggler').on(ace.click_event, function() {
- $('#sidebar').toggleClass('display');
- $(this).toggleClass('display');
- return false;
- });
- //mini
- var $minimized = $('#sidebar').hasClass('menu-min');
- $('#sidebar-collapse').on(ace.click_event, function(){
- $minimized = $('#sidebar').hasClass('menu-min');
- ace.settings.sidebar_collapsed(!$minimized);//@ ace-extra.js
- });
+ $('#menu-toggler').on(ace.click_event, function() {
+ $('#sidebar').toggleClass('display');
+ $(this).toggleClass('display');
+ return false;
+ });
+ //mini
+ var $minimized = $('#sidebar').hasClass('menu-min');
+ $('#sidebar-collapse').on(ace.click_event, function(){
+ $minimized = $('#sidebar').hasClass('menu-min');
+ ace.settings.sidebar_collapsed(!$minimized);//@ ace-extra.js
+ });
- var touch = "ontouchend" in document;
- //opening submenu
- $('.nav-list').on(ace.click_event, function(e){
- //check to see if we have clicked on an element which is inside a .dropdown-toggle element?!
- //if so, it means we should toggle a submenu
- var link_element = $(e.target).closest('a');
- if(!link_element || link_element.length == 0) return;//if not clicked inside a link element
-
- $minimized = $('#sidebar').hasClass('menu-min');
-
- if(! link_element.hasClass('dropdown-toggle') ) {//it doesn't have a submenu return
- //just one thing before we return
- //if sidebar is collapsed(minimized) and we click on a first level menu item
- //and the click is on the icon, not on the menu text then let's cancel event and cancel navigation
- //Good for touch devices, that when the icon is tapped to see the menu text, navigation is cancelled
- //navigation is only done when menu text is tapped
- if($minimized && ace.click_event == "tap" &&
- link_element.get(0).parentNode.parentNode == this /*.nav-list*/ )//i.e. only level-1 links
- {
- var text = link_element.find('.menu-text').get(0);
- if( e.target != text && !$.contains(text , e.target) )//not clicking on the text or its children
- return false;
- }
+ var touch = "ontouchend" in document;
+ //opening submenu
+ $('.nav-list').on(ace.click_event, function(e){
+ //check to see if we have clicked on an element which is inside a .dropdown-toggle element?!
+ //if so, it means we should toggle a submenu
+ var link_element = $(e.target).closest('a');
+ if(!link_element || link_element.length == 0) return;//if not clicked inside a link element
+
+ $minimized = $('#sidebar').hasClass('menu-min');
+
+ if(! link_element.hasClass('dropdown-toggle') ) {//it doesn't have a submenu return
+ //just one thing before we return
+ //if sidebar is collapsed(minimized) and we click on a first level menu item
+ //and the click is on the icon, not on the menu text then let's cancel event and cancel navigation
+ //Good for touch devices, that when the icon is tapped to see the menu text, navigation is cancelled
+ //navigation is only done when menu text is tapped
+ if($minimized && ace.click_event == "tap" &&
+ link_element.get(0).parentNode.parentNode == this /*.nav-list*/ )//i.e. only level-1 links
+ {
+ var text = link_element.find('.menu-text').get(0);
+ if( e.target != text && !$.contains(text , e.target) )//not clicking on the text or its children
+ return false;
+ }
- return;
- }
- //
- var sub = link_element.next().get(0);
+ return;
+ }
+ //
+ var sub = link_element.next().get(0);
- //if we are opening this submenu, close all other submenus except the ".active" one
- if(! $(sub).is(':visible') ) {//if not open and visible, let's open it and make it visible
- var parent_ul = $(sub.parentNode).closest('ul');
- if($minimized && parent_ul.hasClass('nav-list')) return;
-
- parent_ul.find('> .open > .submenu').each(function(){
- //close all other open submenus except for the active one
- if(this != sub && !$(this.parentNode).hasClass('active')) {
- $(this).slideUp(200).parent().removeClass('open');
-
- //uncomment the following line to close all submenus on deeper levels when closing a submenu
- //$(this).find('.open > .submenu').slideUp(0).parent().removeClass('open');
- }
- });
- } else {
- //uncomment the following line to close all submenus on deeper levels when closing a submenu
- //$(sub).find('.open > .submenu').slideUp(0).parent().removeClass('open');
- }
+ //if we are opening this submenu, close all other submenus except the ".active" one
+ if(! $(sub).is(':visible') ) {//if not open and visible, let's open it and make it visible
+ var parent_ul = $(sub.parentNode).closest('ul');
+ if($minimized && parent_ul.hasClass('nav-list')) return;
+
+ parent_ul.find('> .open > .submenu').each(function(){
+ //close all other open submenus except for the active one
+ if(this != sub && !$(this.parentNode).hasClass('active')) {
+ $(this).slideUp(200).parent().removeClass('open');
+
+ //uncomment the following line to close all submenus on deeper levels when closing a submenu
+ //$(this).find('.open > .submenu').slideUp(0).parent().removeClass('open');
+ }
+ });
+ } else {
+ //uncomment the following line to close all submenus on deeper levels when closing a submenu
+ //$(sub).find('.open > .submenu').slideUp(0).parent().removeClass('open');
+ }
- if($minimized && $(sub.parentNode.parentNode).hasClass('nav-list')) return false;
+ if($minimized && $(sub.parentNode.parentNode).hasClass('nav-list')) return false;
- $(sub).slideToggle(200).parent().toggleClass('open');
- return false;
- })
+ $(sub).slideToggle(200).parent().toggleClass('open');
+ return false;
+ })
}
ace.general_things = function($) {
$('.ace-nav [class*="icon-animated-"]').closest('a').on('click', function(){
- var icon = $(this).find('[class*="icon-animated-"]').eq(0);
- var $match = icon.attr('class').match(/icon\-animated\-([\d\w]+)/);
- icon.removeClass($match[0]);
- $(this).off('click');
+ var icon = $(this).find('[class*="icon-animated-"]').eq(0);
+ var $match = icon.attr('class').match(/icon\-animated\-([\d\w]+)/);
+ icon.removeClass($match[0]);
+ $(this).off('click');
});
$('.nav-list .badge[title],.nav-list .label[title]').tooltip({'placement':'right'});
@@ -89,73 +89,73 @@ ace.general_things = function($) {
//simple settings
$('#ace-settings-btn').on(ace.click_event, function(){
- $(this).toggleClass('open');
- $('#ace-settings-box').toggleClass('open');
+ $(this).toggleClass('open');
+ $('#ace-settings-box').toggleClass('open');
});
$('#ace-settings-navbar').on('click', function(){
- ace.settings.navbar_fixed(this.checked);//@ ace-extra.js
+ ace.settings.navbar_fixed(this.checked);//@ ace-extra.js
}).each(function(){this.checked = ace.settings.is('navbar', 'fixed')})
$('#ace-settings-sidebar').on('click', function(){
- ace.settings.sidebar_fixed(this.checked);//@ ace-extra.js
+ ace.settings.sidebar_fixed(this.checked);//@ ace-extra.js
}).each(function(){this.checked = ace.settings.is('sidebar', 'fixed')})
$('#ace-settings-breadcrumbs').on('click', function(){
- ace.settings.breadcrumbs_fixed(this.checked);//@ ace-extra.js
+ ace.settings.breadcrumbs_fixed(this.checked);//@ ace-extra.js
}).each(function(){this.checked = ace.settings.is('breadcrumbs', 'fixed')})
$('#ace-settings-add-container').on('click', function(){
- ace.settings.main_container_fixed(this.checked);//@ ace-extra.js
+ ace.settings.main_container_fixed(this.checked);//@ ace-extra.js
}).each(function(){this.checked = ace.settings.is('main-container', 'fixed')})
//Switching to RTL (right to left) Mode
$('#ace-settings-rtl').removeAttr('checked').on('click', function(){
- ace.switch_direction(jQuery);
+ ace.switch_direction(jQuery);
});
$('#btn-scroll-up').on(ace.click_event, function(){
- var duration = Math.min(400, Math.max(100, parseInt($('html').scrollTop() / 3)));
- $('html,body').animate({scrollTop: 0}, duration);
- return false;
+ var duration = Math.min(400, Math.max(100, parseInt($('html').scrollTop() / 3)));
+ $('html,body').animate({scrollTop: 0}, duration);
+ return false;
});
try {
- $('#skin-colorpicker').ace_colorpicker();
+ $('#skin-colorpicker').ace_colorpicker();
} catch(e) {}
$('#skin-colorpicker').on('change', function(){
- var skin_class = $(this).find('option:selected').data('skin');
+ var skin_class = $(this).find('option:selected').data('skin');
- var body = $(document.body);
- body.removeClass('skin-1 skin-2 skin-3');
+ var body = $(document.body);
+ body.removeClass('skin-1 skin-2 skin-3');
- if(skin_class != 'default') body.addClass(skin_class);
+ if(skin_class != 'default') body.addClass(skin_class);
- if(skin_class == 'skin-1') {
- $('.ace-nav > li.grey').addClass('dark');
- }
- else {
- $('.ace-nav > li.grey').removeClass('dark');
- }
+ if(skin_class == 'skin-1') {
+ $('.ace-nav > li.grey').addClass('dark');
+ }
+ else {
+ $('.ace-nav > li.grey').removeClass('dark');
+ }
- if(skin_class == 'skin-2') {
- $('.ace-nav > li').addClass('no-border margin-1');
- $('.ace-nav > li:not(:last-child)').addClass('light-pink').find('> a > [class*="icon-"]').addClass('pink').end().eq(0).find('.badge').addClass('badge-warning');
- }
- else {
- $('.ace-nav > li').removeClass('no-border margin-1');
- $('.ace-nav > li:not(:last-child)').removeClass('light-pink').find('> a > [class*="icon-"]').removeClass('pink').end().eq(0).find('.badge').removeClass('badge-warning');
- }
+ if(skin_class == 'skin-2') {
+ $('.ace-nav > li').addClass('no-border margin-1');
+ $('.ace-nav > li:not(:last-child)').addClass('light-pink').find('> a > [class*="icon-"]').addClass('pink').end().eq(0).find('.badge').addClass('badge-warning');
+ }
+ else {
+ $('.ace-nav > li').removeClass('no-border margin-1');
+ $('.ace-nav > li:not(:last-child)').removeClass('light-pink').find('> a > [class*="icon-"]').removeClass('pink').end().eq(0).find('.badge').removeClass('badge-warning');
+ }
- if(skin_class == 'skin-3') {
- $('.ace-nav > li.grey').addClass('red').find('.badge').addClass('badge-yellow');
- } else {
- $('.ace-nav > li.grey').removeClass('red').find('.badge').removeClass('badge-yellow');
- }
+ if(skin_class == 'skin-3') {
+ $('.ace-nav > li.grey').addClass('red').find('.badge').addClass('badge-yellow');
+ } else {
+ $('.ace-nav > li.grey').removeClass('red').find('.badge').removeClass('badge-yellow');
+ }
});
};
@@ -163,224 +163,224 @@ ace.general_things = function($) {
ace.widget_boxes = function($) {
- $(document).on('hide.bs.collapse show.bs.collapse', function (ev) {
- var hidden_id = ev.target.getAttribute('id')
- $('[href*="#'+ hidden_id+'"]').find('[class*="icon-"]').each(function(){
- var $icon = $(this)
+ $(document).on('hide.bs.collapse show.bs.collapse', function (ev) {
+ var hidden_id = ev.target.getAttribute('id')
+ $('[href*="#'+ hidden_id+'"]').find('[class*="icon-"]').each(function(){
+ var $icon = $(this)
- var $match
- var $icon_down = null
- var $icon_up = null
- if( ($icon_down = $icon.attr('data-icon-show')) ) {
- $icon_up = $icon.attr('data-icon-hide')
- }
- else if( $match = $icon.attr('class').match(/icon\-(.*)\-(up|down)/) ) {
- $icon_down = 'icon-'+$match[1]+'-down'
- $icon_up = 'icon-'+$match[1]+'-up'
- }
+ var $match
+ var $icon_down = null
+ var $icon_up = null
+ if( ($icon_down = $icon.attr('data-icon-show')) ) {
+ $icon_up = $icon.attr('data-icon-hide')
+ }
+ else if( $match = $icon.attr('class').match(/icon\-(.*)\-(up|down)/) ) {
+ $icon_down = 'icon-'+$match[1]+'-down'
+ $icon_up = 'icon-'+$match[1]+'-up'
+ }
- if($icon_down) {
- if(ev.type == 'show') $icon.removeClass($icon_down).addClass($icon_up)
- else $icon.removeClass($icon_up).addClass($icon_down)
-
- return false;//ignore other icons that match, one is enough
- }
+ if($icon_down) {
+ if(ev.type == 'show') $icon.removeClass($icon_down).addClass($icon_up)
+ else $icon.removeClass($icon_up).addClass($icon_down)
+
+ return false;//ignore other icons that match, one is enough
+ }
- });
- });
+ });
+ });
- $(document).on('click.ace.widget', '[data-action]', function (ev) {
- ev.preventDefault();
+ $(document).on('click.ace.widget', '[data-action]', function (ev) {
+ ev.preventDefault();
- var $this = $(this);
- var $action = $this.data('action');
- var $box = $this.closest('.widget-box');
+ var $this = $(this);
+ var $action = $this.data('action');
+ var $box = $this.closest('.widget-box');
- if($box.hasClass('ui-sortable-helper')) return;
+ if($box.hasClass('ui-sortable-helper')) return;
- if($action == 'collapse') {
- var event_name = $box.hasClass('collapsed') ? 'show' : 'hide';
- var event_complete_name = event_name == 'show' ? 'shown' : 'hidden';
+ if($action == 'collapse') {
+ var event_name = $box.hasClass('collapsed') ? 'show' : 'hide';
+ var event_complete_name = event_name == 'show' ? 'shown' : 'hidden';
-
- var event
- $box.trigger(event = $.Event(event_name+'.ace.widget'))
- if (event.isDefaultPrevented()) return
-
- var $body = $box.find('.widget-body');
- var $icon = $this.find('[class*=icon-]').eq(0);
- var $match = $icon.attr('class').match(/icon\-(.*)\-(up|down)/);
- var $icon_down = 'icon-'+$match[1]+'-down';
- var $icon_up = 'icon-'+$match[1]+'-up';
-
- var $body_inner = $body.find('.widget-body-inner')
- if($body_inner.length == 0) {
- $body = $body.wrapInner('
').find(':first-child').eq(0);
- } else $body = $body_inner.eq(0);
+
+ var event
+ $box.trigger(event = $.Event(event_name+'.ace.widget'))
+ if (event.isDefaultPrevented()) return
+
+ var $body = $box.find('.widget-body');
+ var $icon = $this.find('[class*=icon-]').eq(0);
+ var $match = $icon.attr('class').match(/icon\-(.*)\-(up|down)/);
+ var $icon_down = 'icon-'+$match[1]+'-down';
+ var $icon_up = 'icon-'+$match[1]+'-up';
+
+ var $body_inner = $body.find('.widget-body-inner')
+ if($body_inner.length == 0) {
+ $body = $body.wrapInner('
').find(':first-child').eq(0);
+ } else $body = $body_inner.eq(0);
- var expandSpeed = 300;
- var collapseSpeed = 200;
+ var expandSpeed = 300;
+ var collapseSpeed = 200;
- if( event_name == 'show' ) {
- if($icon) $icon.addClass($icon_up).removeClass($icon_down);
- $box.removeClass('collapsed');
- $body.slideUp(0 , function(){$body.slideDown(expandSpeed, function(){$box.trigger(event = $.Event(event_complete_name+'.ace.widget'))})});
- }
- else {
- if($icon) $icon.addClass($icon_down).removeClass($icon_up);
- $body.slideUp(collapseSpeed, function(){$box.addClass('collapsed');$box.trigger(event = $.Event(event_complete_name+'.ace.widget'))});
- }
+ if( event_name == 'show' ) {
+ if($icon) $icon.addClass($icon_up).removeClass($icon_down);
+ $box.removeClass('collapsed');
+ $body.slideUp(0 , function(){$body.slideDown(expandSpeed, function(){$box.trigger(event = $.Event(event_complete_name+'.ace.widget'))})});
+ }
+ else {
+ if($icon) $icon.addClass($icon_down).removeClass($icon_up);
+ $body.slideUp(collapseSpeed, function(){$box.addClass('collapsed');$box.trigger(event = $.Event(event_complete_name+'.ace.widget'))});
+ }
-
- }
- else if($action == 'close') {
- var event
- $box.trigger(event = $.Event('close.ace.widget'))
- if (event.isDefaultPrevented()) return
-
- var closeSpeed = parseInt($this.data('close-speed')) || 300;
- $box.hide(closeSpeed , function(){$box.trigger(event = $.Event('closed.ace.widget'));$box.remove();});
- }
- else if($action == 'reload') {
- var event
- $box.trigger(event = $.Event('reload.ace.widget'))
- if (event.isDefaultPrevented()) return
+
+ }
+ else if($action == 'close') {
+ var event
+ $box.trigger(event = $.Event('close.ace.widget'))
+ if (event.isDefaultPrevented()) return
+
+ var closeSpeed = parseInt($this.data('close-speed')) || 300;
+ $box.hide(closeSpeed , function(){$box.trigger(event = $.Event('closed.ace.widget'));$box.remove();});
+ }
+ else if($action == 'reload') {
+ var event
+ $box.trigger(event = $.Event('reload.ace.widget'))
+ if (event.isDefaultPrevented()) return
- $this.blur();
+ $this.blur();
- var $remove = false;
- if($box.css('position') == 'static') {$remove = true; $box.addClass('position-relative');}
- $box.append('
');
-
- $box.one('reloaded.ace.widget', function() {
- $box.find('.widget-box-overlay').remove();
- if($remove) $box.removeClass('position-relative');
- });
+ var $remove = false;
+ if($box.css('position') == 'static') {$remove = true; $box.addClass('position-relative');}
+ $box.append('
');
+
+ $box.one('reloaded.ace.widget', function() {
+ $box.find('.widget-box-overlay').remove();
+ if($remove) $box.removeClass('position-relative');
+ });
- }
- else if($action == 'settings') {
- var event = $.Event('settings.ace.widget')
- $box.trigger(event)
- }
+ }
+ else if($action == 'settings') {
+ var event = $.Event('settings.ace.widget')
+ $box.trigger(event)
+ }
- });
+ });
};
ace.widget_reload_handler = function($) {
- //***default action for reload in this demo
- //you should remove this and add your own handler for each specific .widget-box
- //when data is finished loading or processing is done you can call $box.trigger('reloaded.ace.widget')
- $(document).on('reload.ace.widget', '.widget-box', function (ev) {
- var $box = $(this);
- //trigger the reloaded event after 1-2 seconds
- setTimeout(function() {
- $box.trigger('reloaded.ace.widget');
- }, parseInt(Math.random() * 1000 + 1000));
- });
-
-
- //you may want to do something like this:
- /**
- $('#my-widget-box').on('reload.ace.widget', function(){
- //load new data
- //when finished trigger "reloaded"
- $(this).trigger('reloaded.ace.widget');
- });
- */
+ //***default action for reload in this demo
+ //you should remove this and add your own handler for each specific .widget-box
+ //when data is finished loading or processing is done you can call $box.trigger('reloaded.ace.widget')
+ $(document).on('reload.ace.widget', '.widget-box', function (ev) {
+ var $box = $(this);
+ //trigger the reloaded event after 1-2 seconds
+ setTimeout(function() {
+ $box.trigger('reloaded.ace.widget');
+ }, parseInt(Math.random() * 1000 + 1000));
+ });
+
+
+ //you may want to do something like this:
+ /**
+ $('#my-widget-box').on('reload.ace.widget', function(){
+ //load new data
+ //when finished trigger "reloaded"
+ $(this).trigger('reloaded.ace.widget');
+ });
+ */
};
//search box's dropdown autocomplete
ace.enable_search_ahead = function($) {
- ace.variable_US_STATES = ["Alabama","Alaska","Arizona","Arkansas","California","Colorado","Connecticut","Delaware","Florida","Georgia","Hawaii","Idaho","Illinois","Indiana","Iowa","Kansas","Kentucky","Louisiana","Maine","Maryland","Massachusetts","Michigan","Minnesota","Mississippi","Missouri","Montana","Nebraska","Nevada","New Hampshire","New Jersey","New Mexico","New York","North Dakota","North Carolina","Ohio","Oklahoma","Oregon","Pennsylvania","Rhode Island","South Carolina","South Dakota","Tennessee","Texas","Utah","Vermont","Virginia","Washington","West Virginia","Wisconsin","Wyoming"]
-
- try {
- $('#nav-search-input').typeahead({
- source: ace.variable_US_STATES,
- updater:function (item) {
- $('#nav-search-input').focus();
- return item;
- }
- });
- } catch(e) {}
+ ace.variable_US_STATES = ["Alabama","Alaska","Arizona","Arkansas","California","Colorado","Connecticut","Delaware","Florida","Georgia","Hawaii","Idaho","Illinois","Indiana","Iowa","Kansas","Kentucky","Louisiana","Maine","Maryland","Massachusetts","Michigan","Minnesota","Mississippi","Missouri","Montana","Nebraska","Nevada","New Hampshire","New Jersey","New Mexico","New York","North Dakota","North Carolina","Ohio","Oklahoma","Oregon","Pennsylvania","Rhode Island","South Carolina","South Dakota","Tennessee","Texas","Utah","Vermont","Virginia","Washington","West Virginia","Wisconsin","Wyoming"]
+
+ try {
+ $('#nav-search-input').typeahead({
+ source: ace.variable_US_STATES,
+ updater:function (item) {
+ $('#nav-search-input').focus();
+ return item;
+ }
+ });
+ } catch(e) {}
};
ace.switch_direction = function($) {
- var $body = $(document.body);
- $body
- .toggleClass('rtl')
- //toggle pull-right class on dropdown-menu
- .find('.dropdown-menu:not(.datepicker-dropdown,.colorpicker)').toggleClass('pull-right')
- .end()
- //swap pull-left & pull-right
- .find('.pull-right:not(.dropdown-menu,blockquote,.profile-skills .pull-right)').removeClass('pull-right').addClass('tmp-rtl-pull-right')
- .end()
- .find('.pull-left:not(.dropdown-submenu,.profile-skills .pull-left)').removeClass('pull-left').addClass('pull-right')
- .end()
- .find('.tmp-rtl-pull-right').removeClass('tmp-rtl-pull-right').addClass('pull-left')
- .end()
-
- .find('.chosen-container').toggleClass('chosen-rtl')
- .end()
+ var $body = $(document.body);
+ $body
+ .toggleClass('rtl')
+ //toggle pull-right class on dropdown-menu
+ .find('.dropdown-menu:not(.datepicker-dropdown,.colorpicker)').toggleClass('pull-right')
+ .end()
+ //swap pull-left & pull-right
+ .find('.pull-right:not(.dropdown-menu,blockquote,.profile-skills .pull-right)').removeClass('pull-right').addClass('tmp-rtl-pull-right')
+ .end()
+ .find('.pull-left:not(.dropdown-submenu,.profile-skills .pull-left)').removeClass('pull-left').addClass('pull-right')
+ .end()
+ .find('.tmp-rtl-pull-right').removeClass('tmp-rtl-pull-right').addClass('pull-left')
+ .end()
+
+ .find('.chosen-container').toggleClass('chosen-rtl')
+ .end()
- function swap_classes(class1, class2) {
- $body
- .find('.'+class1).removeClass(class1).addClass('tmp-rtl-'+class1)
- .end()
- .find('.'+class2).removeClass(class2).addClass(class1)
- .end()
- .find('.tmp-rtl-'+class1).removeClass('tmp-rtl-'+class1).addClass(class2)
- }
- function swap_styles(style1, style2, elements) {
- elements.each(function(){
- var e = $(this);
- var tmp = e.css(style2);
- e.css(style2 , e.css(style1));
- e.css(style1 , tmp);
- });
- }
+ function swap_classes(class1, class2) {
+ $body
+ .find('.'+class1).removeClass(class1).addClass('tmp-rtl-'+class1)
+ .end()
+ .find('.'+class2).removeClass(class2).addClass(class1)
+ .end()
+ .find('.tmp-rtl-'+class1).removeClass('tmp-rtl-'+class1).addClass(class2)
+ }
+ function swap_styles(style1, style2, elements) {
+ elements.each(function(){
+ var e = $(this);
+ var tmp = e.css(style2);
+ e.css(style2 , e.css(style1));
+ e.css(style1 , tmp);
+ });
+ }
- swap_classes('align-left', 'align-right');
- swap_classes('no-padding-left', 'no-padding-right');
- swap_classes('arrowed', 'arrowed-right');
- swap_classes('arrowed-in', 'arrowed-in-right');
- swap_classes('messagebar-item-left', 'messagebar-item-right');//for inbox page
+ swap_classes('align-left', 'align-right');
+ swap_classes('no-padding-left', 'no-padding-right');
+ swap_classes('arrowed', 'arrowed-right');
+ swap_classes('arrowed-in', 'arrowed-in-right');
+ swap_classes('messagebar-item-left', 'messagebar-item-right');//for inbox page
- //redraw the traffic pie chart on homepage with a different parameter
- var placeholder = $('#piechart-placeholder');
- if(placeholder.size() > 0) {
- var pos = $(document.body).hasClass('rtl') ? 'nw' : 'ne';//draw on north-west or north-east?
- placeholder.data('draw').call(placeholder.get(0) , placeholder, placeholder.data('chart'), pos);
- }
+ //redraw the traffic pie chart on homepage with a different parameter
+ var placeholder = $('#piechart-placeholder');
+ if(placeholder.size() > 0) {
+ var pos = $(document.body).hasClass('rtl') ? 'nw' : 'ne';//draw on north-west or north-east?
+ placeholder.data('draw').call(placeholder.get(0) , placeholder, placeholder.data('chart'), pos);
+ }
};
jQuery(function($) {
- //ace.click_event defined in ace-elements.js
- ace.handle_side_menu(jQuery);
+ //ace.click_event defined in ace-elements.js
+ ace.handle_side_menu(jQuery);
- ace.enable_search_ahead(jQuery);
+ ace.enable_search_ahead(jQuery);
- ace.general_things(jQuery);//and settings
+ ace.general_things(jQuery);//and settings
- ace.widget_boxes(jQuery);
- ace.widget_reload_handler(jQuery);//this is for demo only, you can remove and have your own function, please see examples/widget.html
+ ace.widget_boxes(jQuery);
+ ace.widget_reload_handler(jQuery);//this is for demo only, you can remove and have your own function, please see examples/widget.html
- /**
- //make sidebar scrollbar when it is fixed and some parts of it is out of view
- //>> you should include jquery-ui and slimscroll javascript files in your file
- //>> you can call this function when sidebar is clicked to be fixed
- $('.nav-list').slimScroll({
- height: '400px',
- distance:0,
- size : '6px'
- });
- */
+ /**
+ //make sidebar scrollbar when it is fixed and some parts of it is out of view
+ //>> you should include jquery-ui and slimscroll javascript files in your file
+ //>> you can call this function when sidebar is clicked to be fixed
+ $('.nav-list').slimScroll({
+ height: '400px',
+ distance:0,
+ size : '6px'
+ });
+ */
});
diff --git a/static/js/lib/backbone-orm.js b/static/js/lib/backbone-orm.js
new file mode 100644
index 0000000..d683c93
--- /dev/null
+++ b/static/js/lib/backbone-orm.js
@@ -0,0 +1,6938 @@
+/*
+ backbone-orm.js 0.5.3
+ Copyright (c) 2013 Vidigami - https://github.com/vidigami/backbone-orm
+ License: MIT (http://www.opensource.org/licenses/mit-license.php)
+ Dependencies: Backbone.js, Underscore.js, Moment.js, and Inflection.js.
+*/
+(function() {
+var globals = {};
+
+/* local-only brunch-like require (based on https://github.com/brunch/commonjs-require-definition) */
+(function() {
+ 'use strict';
+
+ var modules = {};
+ var cache = {};
+
+ var has = function(object, name) {
+ return ({}).hasOwnProperty.call(object, name);
+ };
+
+ var expand = function(root, name) {
+ var results = [], parts, part;
+ if (/^\.\.?(\/|$)/.test(name)) {
+ parts = [root, name].join('/').split('/');
+ } else {
+ parts = name.split('/');
+ }
+ for (var i = 0, length = parts.length; i < length; i++) {
+ part = parts[i];
+ if (part === '..') {
+ results.pop();
+ } else if (part !== '.' && part !== '') {
+ results.push(part);
+ }
+ }
+ return results.join('/');
+ };
+
+ var dirname = function(path) {
+ return path.split('/').slice(0, -1).join('/');
+ };
+
+ var localRequire = function(path) {
+ var _require = function(name) {
+ var dir = dirname(path);
+ var absolute = expand(dir, name);
+ return globals.require(absolute, path);
+ };
+ _require.register = globals.require.register;
+ _require.shim = globals.require.shim;
+ return _require;
+ };
+
+ var initModule = function(name, definition) {
+ var module = {id: name, exports: {}};
+ cache[name] = module;
+ definition(module.exports, localRequire(name), module);
+ return module.exports;
+ };
+
+ var require = function(name, loaderPath) {
+ var path = expand(name, '.');
+ if (loaderPath == null) loaderPath = '/';
+
+ if (has(cache, path)) return cache[path].exports;
+ if (has(modules, path)) return initModule(path, modules[path]);
+
+ var dirIndex = expand(path, './index');
+ if (has(cache, dirIndex)) return cache[dirIndex].exports;
+ if (has(modules, dirIndex)) return initModule(dirIndex, modules[dirIndex]);
+
+ throw new Error('Cannot find module "' + name + '" from '+ '"' + loaderPath + '"');
+ };
+
+ var define = function(bundle, fn) {
+ if (typeof bundle === 'object') {
+ for (var key in bundle) {
+ if (has(bundle, key)) {
+ modules[key] = bundle[key];
+ }
+ }
+ } else {
+ modules[bundle] = fn;
+ }
+ };
+
+ var toString = Object.prototype.toString;
+ var isArray = function(obj) { return toString.call(obj) === '[object Array]'; }
+
+ // client shimming to add to local module system
+ var shim = function(info) {
+ if (typeof window === "undefined") return;
+ if (!isArray(info)) info = [info];
+
+ var iterator = function(item) {
+ var dep;
+
+ // already registered with local require
+ try { if (globals.require(item.path)) { return; } } catch (e) {}
+
+ // use global require
+ try { dep = typeof window.require === "function" ? window.require(item.path) : void 0; } catch (e) {}
+
+ // use symbol path on window
+ if (!dep && item.symbol) {
+ var components = item.symbol.split('.');
+ dep = window;
+ for (var i = 0, length = components.length; i < length; i++) { if (!(dep = dep[components[i]])) break; }
+ }
+
+ // not found
+ if (!dep) {
+ if (item.optional) return;
+ throw new Error("Missing dependency: " + item.path);
+ }
+
+ // register with local require
+ globals.require.register(item.path, (function(exports, require, module) { return module.exports = dep; }));
+ if (item.alias) { globals.require.register(item.alias, (function(exports, require, module) { return module.exports = dep; })); }
+ };
+
+ for (var i = 0, length = info.length; i < length; i++) { iterator(info[i]); }
+ };
+
+ globals.require = require;
+ globals.require.register = define;
+ globals.require.shim = shim;
+}).call(this);
+var require = globals.require;
+
+require.register("_shims", function(exports, require, module) {
+//
+// The shims in this file are not fully implemented shims for the ES5
+// features, but do work for the particular usecases there is in
+// the other modules.
+//
+
+var toString = Object.prototype.toString;
+var hasOwnProperty = Object.prototype.hasOwnProperty;
+
+// Array.isArray is supported in IE9
+function isArray(xs) {
+ return toString.call(xs) === '[object Array]';
+}
+exports.isArray = typeof Array.isArray === 'function' ? Array.isArray : isArray;
+
+// Array.prototype.indexOf is supported in IE9
+exports.indexOf = function indexOf(xs, x) {
+ if (xs.indexOf) return xs.indexOf(x);
+ for (var i = 0; i < xs.length; i++) {
+ if (x === xs[i]) return i;
+ }
+ return -1;
+};
+
+// Array.prototype.filter is supported in IE9
+exports.filter = function filter(xs, fn) {
+ if (xs.filter) return xs.filter(fn);
+ var res = [];
+ for (var i = 0; i < xs.length; i++) {
+ if (fn(xs[i], i, xs)) res.push(xs[i]);
+ }
+ return res;
+};
+
+// Array.prototype.forEach is supported in IE9
+exports.forEach = function forEach(xs, fn, self) {
+ if (xs.forEach) return xs.forEach(fn, self);
+ for (var i = 0; i < xs.length; i++) {
+ fn.call(self, xs[i], i, xs);
+ }
+};
+
+// Array.prototype.map is supported in IE9
+exports.map = function map(xs, fn) {
+ if (xs.map) return xs.map(fn);
+ var out = new Array(xs.length);
+ for (var i = 0; i < xs.length; i++) {
+ out[i] = fn(xs[i], i, xs);
+ }
+ return out;
+};
+
+// Array.prototype.reduce is supported in IE9
+exports.reduce = function reduce(array, callback, opt_initialValue) {
+ if (array.reduce) return array.reduce(callback, opt_initialValue);
+ var value, isValueSet = false;
+
+ if (2 < arguments.length) {
+ value = opt_initialValue;
+ isValueSet = true;
+ }
+ for (var i = 0, l = array.length; l > i; ++i) {
+ if (array.hasOwnProperty(i)) {
+ if (isValueSet) {
+ value = callback(value, array[i], i, array);
+ }
+ else {
+ value = array[i];
+ isValueSet = true;
+ }
+ }
+ }
+
+ return value;
+};
+
+// String.prototype.substr - negative index don't work in IE8
+if ('ab'.substr(-1) !== 'b') {
+ exports.substr = function (str, start, length) {
+ // did we get a negative start, calculate how much it is from the beginning of the string
+ if (start < 0) start = str.length + start;
+
+ // call the original function
+ return str.substr(start, length);
+ };
+} else {
+ exports.substr = function (str, start, length) {
+ return str.substr(start, length);
+ };
+}
+
+// String.prototype.trim is supported in IE9
+exports.trim = function (str) {
+ if (str.trim) return str.trim();
+ return str.replace(/^\s+|\s+$/g, '');
+};
+
+// Function.prototype.bind is supported in IE9
+exports.bind = function () {
+ var args = Array.prototype.slice.call(arguments);
+ var fn = args.shift();
+ if (fn.bind) return fn.bind.apply(fn, args);
+ var self = args.shift();
+ return function () {
+ fn.apply(self, args.concat([Array.prototype.slice.call(arguments)]));
+ };
+};
+
+// Object.create is supported in IE9
+function create(prototype, properties) {
+ var object;
+ if (prototype === null) {
+ object = { '__proto__' : null };
+ }
+ else {
+ if (typeof prototype !== 'object') {
+ throw new TypeError(
+ 'typeof prototype[' + (typeof prototype) + '] != \'object\''
+ );
+ }
+ var Type = function () {};
+ Type.prototype = prototype;
+ object = new Type();
+ object.__proto__ = prototype;
+ }
+ if (typeof properties !== 'undefined' && Object.defineProperties) {
+ Object.defineProperties(object, properties);
+ }
+ return object;
+}
+exports.create = typeof Object.create === 'function' ? Object.create : create;
+
+// Object.keys and Object.getOwnPropertyNames is supported in IE9 however
+// they do show a description and number property on Error objects
+function notObject(object) {
+ return ((typeof object != "object" && typeof object != "function") || object === null);
+}
+
+function keysShim(object) {
+ if (notObject(object)) {
+ throw new TypeError("Object.keys called on a non-object");
+ }
+
+ var result = [];
+ for (var name in object) {
+ if (hasOwnProperty.call(object, name)) {
+ result.push(name);
+ }
+ }
+ return result;
+}
+
+// getOwnPropertyNames is almost the same as Object.keys one key feature
+// is that it returns hidden properties, since that can't be implemented,
+// this feature gets reduced so it just shows the length property on arrays
+function propertyShim(object) {
+ if (notObject(object)) {
+ throw new TypeError("Object.getOwnPropertyNames called on a non-object");
+ }
+
+ var result = keysShim(object);
+ if (exports.isArray(object) && exports.indexOf(object, 'length') === -1) {
+ result.push('length');
+ }
+ return result;
+}
+
+var keys = typeof Object.keys === 'function' ? Object.keys : keysShim;
+var getOwnPropertyNames = typeof Object.getOwnPropertyNames === 'function' ?
+ Object.getOwnPropertyNames : propertyShim;
+
+if (new Error().hasOwnProperty('description')) {
+ var ERROR_PROPERTY_FILTER = function (obj, array) {
+ if (toString.call(obj) === '[object Error]') {
+ array = exports.filter(array, function (name) {
+ return name !== 'description' && name !== 'number' && name !== 'message';
+ });
+ }
+ return array;
+ };
+
+ exports.keys = function (object) {
+ return ERROR_PROPERTY_FILTER(object, keys(object));
+ };
+ exports.getOwnPropertyNames = function (object) {
+ return ERROR_PROPERTY_FILTER(object, getOwnPropertyNames(object));
+ };
+} else {
+ exports.keys = keys;
+ exports.getOwnPropertyNames = getOwnPropertyNames;
+}
+
+// Object.getOwnPropertyDescriptor - supported in IE8 but only on dom elements
+function valueObject(value, key) {
+ return { value: value[key] };
+}
+
+if (typeof Object.getOwnPropertyDescriptor === 'function') {
+ try {
+ Object.getOwnPropertyDescriptor({'a': 1}, 'a');
+ exports.getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
+ } catch (e) {
+ // IE8 dom element issue - use a try catch and default to valueObject
+ exports.getOwnPropertyDescriptor = function (value, key) {
+ try {
+ return Object.getOwnPropertyDescriptor(value, key);
+ } catch (e) {
+ return valueObject(value, key);
+ }
+ };
+ }
+} else {
+ exports.getOwnPropertyDescriptor = valueObject;
+}
+});
+
+;require.register("lru-cache", function(exports, require, module) {
+;(function () { // closure for web browsers
+
+if (typeof module === 'object' && module.exports) {
+ module.exports = LRUCache
+} else {
+ // just set the global for non-node platforms.
+ this.LRUCache = LRUCache
+}
+
+function hOP (obj, key) {
+ return Object.prototype.hasOwnProperty.call(obj, key)
+}
+
+function naiveLength () { return 1 }
+
+function LRUCache (options) {
+ if (!(this instanceof LRUCache)) {
+ return new LRUCache(options)
+ }
+
+ var max
+ if (typeof options === 'number') {
+ max = options
+ options = { max: max }
+ }
+
+ if (!options) options = {}
+
+ max = options.max
+
+ var lengthCalculator = options.length || naiveLength
+
+ if (typeof lengthCalculator !== "function") {
+ lengthCalculator = naiveLength
+ }
+
+ if (!max || !(typeof max === "number") || max <= 0 ) {
+ // a little bit silly. maybe this should throw?
+ max = Infinity
+ }
+
+ var allowStale = options.stale || false
+
+ var maxAge = options.maxAge || null
+
+ var dispose = options.dispose
+
+ var cache = Object.create(null) // hash of items by key
+ , lruList = Object.create(null) // list of items in order of use recency
+ , mru = 0 // most recently used
+ , lru = 0 // least recently used
+ , length = 0 // number of items in the list
+ , itemCount = 0
+
+
+ // resize the cache when the max changes.
+ Object.defineProperty(this, "max",
+ { set : function (mL) {
+ if (!mL || !(typeof mL === "number") || mL <= 0 ) mL = Infinity
+ max = mL
+ // if it gets above double max, trim right away.
+ // otherwise, do it whenever it's convenient.
+ if (length > max) trim()
+ }
+ , get : function () { return max }
+ , enumerable : true
+ })
+
+ // resize the cache when the lengthCalculator changes.
+ Object.defineProperty(this, "lengthCalculator",
+ { set : function (lC) {
+ if (typeof lC !== "function") {
+ lengthCalculator = naiveLength
+ length = itemCount
+ for (var key in cache) {
+ cache[key].length = 1
+ }
+ } else {
+ lengthCalculator = lC
+ length = 0
+ for (var key in cache) {
+ cache[key].length = lengthCalculator(cache[key].value)
+ length += cache[key].length
+ }
+ }
+
+ if (length > max) trim()
+ }
+ , get : function () { return lengthCalculator }
+ , enumerable : true
+ })
+
+ Object.defineProperty(this, "length",
+ { get : function () { return length }
+ , enumerable : true
+ })
+
+
+ Object.defineProperty(this, "itemCount",
+ { get : function () { return itemCount }
+ , enumerable : true
+ })
+
+ this.forEach = function (fn, thisp) {
+ thisp = thisp || this
+ var i = 0;
+ for (var k = mru - 1; k >= 0 && i < itemCount; k--) if (lruList[k]) {
+ i++
+ var hit = lruList[k]
+ if (maxAge && (Date.now() - hit.now > maxAge)) {
+ del(hit)
+ if (!allowStale) hit = undefined
+ }
+ if (hit) {
+ fn.call(thisp, hit.value, hit.key, this)
+ }
+ }
+ }
+
+ this.keys = function () {
+ var keys = new Array(itemCount)
+ var i = 0
+ for (var k = mru - 1; k >= 0 && i < itemCount; k--) if (lruList[k]) {
+ var hit = lruList[k]
+ keys[i++] = hit.key
+ }
+ return keys
+ }
+
+ this.values = function () {
+ var values = new Array(itemCount)
+ var i = 0
+ for (var k = mru - 1; k >= 0 && i < itemCount; k--) if (lruList[k]) {
+ var hit = lruList[k]
+ values[i++] = hit.value
+ }
+ return values
+ }
+
+ this.reset = function () {
+ if (dispose) {
+ for (var k in cache) {
+ dispose(k, cache[k].value)
+ }
+ }
+ cache = {}
+ lruList = {}
+ lru = 0
+ mru = 0
+ length = 0
+ itemCount = 0
+ }
+
+ // Provided for debugging/dev purposes only. No promises whatsoever that
+ // this API stays stable.
+ this.dump = function () {
+ return cache
+ }
+
+ this.dumpLru = function () {
+ return lruList
+ }
+
+ this.set = function (key, value) {
+ if (hOP(cache, key)) {
+ // dispose of the old one before overwriting
+ if (dispose) dispose(key, cache[key].value)
+ if (maxAge) cache[key].now = Date.now()
+ cache[key].value = value
+ this.get(key)
+ return true
+ }
+
+ var len = lengthCalculator(value)
+ var age = maxAge ? Date.now() : 0
+ var hit = new Entry(key, value, mru++, len, age)
+
+ // oversized objects fall out of cache automatically.
+ if (hit.length > max) {
+ if (dispose) dispose(key, value)
+ return false
+ }
+
+ length += hit.length
+ lruList[hit.lu] = cache[key] = hit
+ itemCount ++
+
+ if (length > max) trim()
+ return true
+ }
+
+ this.has = function (key) {
+ if (!hOP(cache, key)) return false
+ var hit = cache[key]
+ if (maxAge && (Date.now() - hit.now > maxAge)) {
+ return false
+ }
+ return true
+ }
+
+ this.get = function (key) {
+ return get(key, true)
+ }
+
+ this.peek = function (key) {
+ return get(key, false)
+ }
+
+ function get (key, doUse) {
+ var hit = cache[key]
+ if (hit) {
+ if (maxAge && (Date.now() - hit.now > maxAge)) {
+ del(hit)
+ if (!allowStale) hit = undefined
+ } else {
+ if (doUse) use(hit)
+ }
+ if (hit) hit = hit.value
+ }
+ return hit
+ }
+
+ function use (hit) {
+ shiftLU(hit)
+ hit.lu = mru ++
+ lruList[hit.lu] = hit
+ }
+
+ this.del = function (key) {
+ del(cache[key])
+ }
+
+ function trim () {
+ while (lru < mru && length > max)
+ del(lruList[lru])
+ }
+
+ function shiftLU(hit) {
+ delete lruList[ hit.lu ]
+ while (lru < mru && !lruList[lru]) lru ++
+ }
+
+ function del(hit) {
+ if (hit) {
+ if (dispose) dispose(hit.key, hit.value)
+ length -= hit.length
+ itemCount --
+ delete cache[ hit.key ]
+ shiftLU(hit)
+ }
+ }
+}
+
+// classy, since V8 prefers predictable objects.
+function Entry (key, value, mru, len, age) {
+ this.key = key
+ this.value = value
+ this.lu = mru
+ this.length = len
+ this.now = age
+}
+
+})()
+
+});
+
+;require.register("querystring", function(exports, require, module) {
+// Copyright Joyent, Inc. and other Node contributors.
+//
+// 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.
+
+// Query String Utilities
+
+var QueryString = exports;
+var util = require('util');
+var shims = require('_shims');
+
+// If obj.hasOwnProperty has been overridden, then calling
+// obj.hasOwnProperty(prop) will break.
+// See: https://github.com/joyent/node/issues/1707
+function hasOwnProperty(obj, prop) {
+return Object.prototype.hasOwnProperty.call(obj, prop);
+}
+
+
+function charCode(c) {
+return c.charCodeAt(0);
+}
+
+// TODO support returning arbitrary buffers.
+
+QueryString.unescape = function(s, decodeSpaces) {
+return decodeURIComponent(s/*, decodeSpaces*/);
+};
+
+
+QueryString.escape = function(str) {
+return encodeURIComponent(str);
+};
+
+var stringifyPrimitive = function(v) {
+if (util.isString(v))
+ return v;
+if (util.isBoolean(v))
+ return v ? 'true' : 'false';
+if (util.isNumber(v))
+ return isFinite(v) ? v : '';
+return '';
+};
+
+
+QueryString.stringify = QueryString.encode = function(obj, sep, eq, name) {
+sep = sep || '&';
+eq = eq || '=';
+if (util.isNull(obj)) {
+ obj = undefined;
+}
+
+if (util.isObject(obj)) {
+ return shims.map(shims.keys(obj), function(k) {
+ var ks = QueryString.escape(stringifyPrimitive(k)) + eq;
+ if (util.isArray(obj[k])) {
+ return shims.map(obj[k], function(v) {
+ return ks + QueryString.escape(stringifyPrimitive(v));
+ }).join(sep);
+ } else {
+ return ks + QueryString.escape(stringifyPrimitive(obj[k]));
+ }
+ }).join(sep);
+
+}
+
+if (!name) return '';
+return QueryString.escape(stringifyPrimitive(name)) + eq +
+ QueryString.escape(stringifyPrimitive(obj));
+};
+
+// Parse a key=val string.
+QueryString.parse = QueryString.decode = function(qs, sep, eq, options) {
+sep = sep || '&';
+eq = eq || '=';
+var obj = {};
+
+if (!util.isString(qs) || qs.length === 0) {
+ return obj;
+}
+
+var regexp = /\+/g;
+qs = qs.split(sep);
+
+var maxKeys = 1000;
+if (options && util.isNumber(options.maxKeys)) {
+ maxKeys = options.maxKeys;
+}
+
+var len = qs.length;
+// maxKeys <= 0 means that we should not limit keys count
+if (maxKeys > 0 && len > maxKeys) {
+ len = maxKeys;
+}
+
+for (var i = 0; i < len; ++i) {
+ var x = qs[i].replace(regexp, '%20'),
+ idx = x.indexOf(eq),
+ kstr, vstr, k, v;
+
+ if (idx >= 0) {
+ kstr = x.substr(0, idx);
+ vstr = x.substr(idx + 1);
+ } else {
+ kstr = x;
+ vstr = '';
+ }
+
+ try {
+ k = decodeURIComponent(kstr);
+ v = decodeURIComponent(vstr);
+ } catch (e) {
+ k = QueryString.unescape(kstr, true);
+ v = QueryString.unescape(vstr, true);
+ }
+
+ if (!hasOwnProperty(obj, k)) {
+ obj[k] = v;
+ } else if (util.isArray(obj[k])) {
+ obj[k].push(v);
+ } else {
+ obj[k] = [obj[k], v];
+ }
+}
+
+return obj;
+};
+});
+
+;require.register("url", function(exports, require, module) {
+// Copyright Joyent, Inc. and other Node contributors.
+//
+// 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.
+
+var punycode = { encode : function (s) { return s } };
+var util = require('util');
+var shims = require('_shims');
+
+exports.parse = urlParse;
+exports.resolve = urlResolve;
+exports.resolveObject = urlResolveObject;
+exports.format = urlFormat;
+
+exports.Url = Url;
+
+function Url() {
+ this.protocol = null;
+ this.slashes = null;
+ this.auth = null;
+ this.host = null;
+ this.port = null;
+ this.hostname = null;
+ this.hash = null;
+ this.search = null;
+ this.query = null;
+ this.pathname = null;
+ this.path = null;
+ this.href = null;
+}
+
+// Reference: RFC 3986, RFC 1808, RFC 2396
+
+// define these here so at least they only have to be
+// compiled once on the first module load.
+var protocolPattern = /^([a-z0-9.+-]+:)/i,
+ portPattern = /:[0-9]*$/,
+
+ // RFC 2396: characters reserved for delimiting URLs.
+ // We actually just auto-escape these.
+ delims = ['<', '>', '"', '`', ' ', '\r', '\n', '\t'],
+
+ // RFC 2396: characters not allowed for various reasons.
+ unwise = ['{', '}', '|', '\\', '^', '`'].concat(delims),
+
+ // Allowed by RFCs, but cause of XSS attacks. Always escape these.
+ autoEscape = ['\''].concat(unwise),
+ // Characters that are never ever allowed in a hostname.
+ // Note that any invalid chars are also handled, but these
+ // are the ones that are *expected* to be seen, so we fast-path
+ // them.
+ nonHostChars = ['%', '/', '?', ';', '#'].concat(autoEscape),
+ hostEndingChars = ['/', '?', '#'],
+ hostnameMaxLen = 255,
+ hostnamePartPattern = /^[a-z0-9A-Z_-]{0,63}$/,
+ hostnamePartStart = /^([a-z0-9A-Z_-]{0,63})(.*)$/,
+ // protocols that can allow "unsafe" and "unwise" chars.
+ unsafeProtocol = {
+ 'javascript': true,
+ 'javascript:': true
+ },
+ // protocols that never have a hostname.
+ hostlessProtocol = {
+ 'javascript': true,
+ 'javascript:': true
+ },
+ // protocols that always contain a // bit.
+ slashedProtocol = {
+ 'http': true,
+ 'https': true,
+ 'ftp': true,
+ 'gopher': true,
+ 'file': true,
+ 'http:': true,
+ 'https:': true,
+ 'ftp:': true,
+ 'gopher:': true,
+ 'file:': true
+ },
+ querystring = require('querystring');
+
+function urlParse(url, parseQueryString, slashesDenoteHost) {
+ if (url && util.isObject(url) && url instanceof Url) return url;
+
+ var u = new Url;
+ u.parse(url, parseQueryString, slashesDenoteHost);
+ return u;
+}
+
+Url.prototype.parse = function(url, parseQueryString, slashesDenoteHost) {
+ if (!util.isString(url)) {
+ throw new TypeError("Parameter 'url' must be a string, not " + typeof url);
+ }
+
+ var rest = url;
+
+ // trim before proceeding.
+ // This is to support parse stuff like " http://foo.com \n"
+ rest = shims.trim(rest);
+
+ var proto = protocolPattern.exec(rest);
+ if (proto) {
+ proto = proto[0];
+ var lowerProto = proto.toLowerCase();
+ this.protocol = lowerProto;
+ rest = rest.substr(proto.length);
+ }
+
+ // figure out if it's got a host
+ // user@server is *always* interpreted as a hostname, and url
+ // resolution will treat //foo/bar as host=foo,path=bar because that's
+ // how the browser resolves relative URLs.
+ if (slashesDenoteHost || proto || rest.match(/^\/\/[^@\/]+@[^@\/]+/)) {
+ var slashes = rest.substr(0, 2) === '//';
+ if (slashes && !(proto && hostlessProtocol[proto])) {
+ rest = rest.substr(2);
+ this.slashes = true;
+ }
+ }
+
+ if (!hostlessProtocol[proto] &&
+ (slashes || (proto && !slashedProtocol[proto]))) {
+
+ // there's a hostname.
+ // the first instance of /, ?, ;, or # ends the host.
+ //
+ // If there is an @ in the hostname, then non-host chars *are* allowed
+ // to the left of the last @ sign, unless some host-ending character
+ // comes *before* the @-sign.
+ // URLs are obnoxious.
+ //
+ // ex:
+ // http://a@b@c/ => user:a@b host:c
+ // http://a@b?@c => user:a host:c path:/?@c
+
+ // v0.12 TODO(isaacs): This is not quite how Chrome does things.
+ // Review our test case against browsers more comprehensively.
+
+ // find the first instance of any hostEndingChars
+ var hostEnd = -1;
+ for (var i = 0; i < hostEndingChars.length; i++) {
+ var hec = rest.indexOf(hostEndingChars[i]);
+ if (hec !== -1 && (hostEnd === -1 || hec < hostEnd))
+ hostEnd = hec;
+ }
+
+ // at this point, either we have an explicit point where the
+ // auth portion cannot go past, or the last @ char is the decider.
+ var auth, atSign;
+ if (hostEnd === -1) {
+ // atSign can be anywhere.
+ atSign = rest.lastIndexOf('@');
+ } else {
+ // atSign must be in auth portion.
+ // http://a@b/c@d => host:b auth:a path:/c@d
+ atSign = rest.lastIndexOf('@', hostEnd);
+ }
+
+ // Now we have a portion which is definitely the auth.
+ // Pull that off.
+ if (atSign !== -1) {
+ auth = rest.slice(0, atSign);
+ rest = rest.slice(atSign + 1);
+ this.auth = decodeURIComponent(auth);
+ }
+
+ // the host is the remaining to the left of the first non-host char
+ hostEnd = -1;
+ for (var i = 0; i < nonHostChars.length; i++) {
+ var hec = rest.indexOf(nonHostChars[i]);
+ if (hec !== -1 && (hostEnd === -1 || hec < hostEnd))
+ hostEnd = hec;
+ }
+ // if we still have not hit it, then the entire thing is a host.
+ if (hostEnd === -1)
+ hostEnd = rest.length;
+
+ this.host = rest.slice(0, hostEnd);
+ rest = rest.slice(hostEnd);
+
+ // pull out port.
+ this.parseHost();
+
+ // we've indicated that there is a hostname,
+ // so even if it's empty, it has to be present.
+ this.hostname = this.hostname || '';
+
+ // if hostname begins with [ and ends with ]
+ // assume that it's an IPv6 address.
+ var ipv6Hostname = this.hostname[0] === '[' &&
+ this.hostname[this.hostname.length - 1] === ']';
+
+ // validate a little.
+ if (!ipv6Hostname) {
+ var hostparts = this.hostname.split(/\./);
+ for (var i = 0, l = hostparts.length; i < l; i++) {
+ var part = hostparts[i];
+ if (!part) continue;
+ if (!part.match(hostnamePartPattern)) {
+ var newpart = '';
+ for (var j = 0, k = part.length; j < k; j++) {
+ if (part.charCodeAt(j) > 127) {
+ // we replace non-ASCII char with a temporary placeholder
+ // we need this to make sure size of hostname is not
+ // broken by replacing non-ASCII by nothing
+ newpart += 'x';
+ } else {
+ newpart += part[j];
+ }
+ }
+ // we test again with ASCII char only
+ if (!newpart.match(hostnamePartPattern)) {
+ var validParts = hostparts.slice(0, i);
+ var notHost = hostparts.slice(i + 1);
+ var bit = part.match(hostnamePartStart);
+ if (bit) {
+ validParts.push(bit[1]);
+ notHost.unshift(bit[2]);
+ }
+ if (notHost.length) {
+ rest = '/' + notHost.join('.') + rest;
+ }
+ this.hostname = validParts.join('.');
+ break;
+ }
+ }
+ }
+ }
+
+ if (this.hostname.length > hostnameMaxLen) {
+ this.hostname = '';
+ } else {
+ // hostnames are always lower case.
+ this.hostname = this.hostname.toLowerCase();
+ }
+
+ if (!ipv6Hostname) {
+ // IDNA Support: Returns a puny coded representation of "domain".
+ // It only converts the part of the domain name that
+ // has non ASCII characters. I.e. it dosent matter if
+ // you call it with a domain that already is in ASCII.
+ var domainArray = this.hostname.split('.');
+ var newOut = [];
+ for (var i = 0; i < domainArray.length; ++i) {
+ var s = domainArray[i];
+ newOut.push(s.match(/[^A-Za-z0-9_-]/) ?
+ 'xn--' + punycode.encode(s) : s);
+ }
+ this.hostname = newOut.join('.');
+ }
+
+ var p = this.port ? ':' + this.port : '';
+ var h = this.hostname || '';
+ this.host = h + p;
+ this.href += this.host;
+
+ // strip [ and ] from the hostname
+ // the host field still retains them, though
+ if (ipv6Hostname) {
+ this.hostname = this.hostname.substr(1, this.hostname.length - 2);
+ if (rest[0] !== '/') {
+ rest = '/' + rest;
+ }
+ }
+ }
+
+ // now rest is set to the post-host stuff.
+ // chop off any delim chars.
+ if (!unsafeProtocol[lowerProto]) {
+
+ // First, make 100% sure that any "autoEscape" chars get
+ // escaped, even if encodeURIComponent doesn't think they
+ // need to be.
+ for (var i = 0, l = autoEscape.length; i < l; i++) {
+ var ae = autoEscape[i];
+ var esc = encodeURIComponent(ae);
+ if (esc === ae) {
+ esc = escape(ae);
+ }
+ rest = rest.split(ae).join(esc);
+ }
+ }
+
+
+ // chop off from the tail first.
+ var hash = rest.indexOf('#');
+ if (hash !== -1) {
+ // got a fragment string.
+ this.hash = rest.substr(hash);
+ rest = rest.slice(0, hash);
+ }
+ var qm = rest.indexOf('?');
+ if (qm !== -1) {
+ this.search = rest.substr(qm);
+ this.query = rest.substr(qm + 1);
+ if (parseQueryString) {
+ this.query = querystring.parse(this.query);
+ }
+ rest = rest.slice(0, qm);
+ } else if (parseQueryString) {
+ // no query string, but parseQueryString still requested
+ this.search = '';
+ this.query = {};
+ }
+ if (rest) this.pathname = rest;
+ if (slashedProtocol[lowerProto] &&
+ this.hostname && !this.pathname) {
+ this.pathname = '/';
+ }
+
+ //to support http.request
+ if (this.pathname || this.search) {
+ var p = this.pathname || '';
+ var s = this.search || '';
+ this.path = p + s;
+ }
+
+ // finally, reconstruct the href based on what has been validated.
+ this.href = this.format();
+ return this;
+};
+
+// format a parsed object into a url string
+function urlFormat(obj) {
+ // ensure it's an object, and not a string url.
+ // If it's an obj, this is a no-op.
+ // this way, you can call url_format() on strings
+ // to clean up potentially wonky urls.
+ if (util.isString(obj)) obj = urlParse(obj);
+ if (!(obj instanceof Url)) return Url.prototype.format.call(obj);
+ return obj.format();
+}
+
+Url.prototype.format = function() {
+ var auth = this.auth || '';
+ if (auth) {
+ auth = encodeURIComponent(auth);
+ auth = auth.replace(/%3A/i, ':');
+ auth += '@';
+ }
+
+ var protocol = this.protocol || '',
+ pathname = this.pathname || '',
+ hash = this.hash || '',
+ host = false,
+ query = '';
+
+ if (this.host) {
+ host = auth + this.host;
+ } else if (this.hostname) {
+ host = auth + (this.hostname.indexOf(':') === -1 ?
+ this.hostname :
+ '[' + this.hostname + ']');
+ if (this.port) {
+ host += ':' + this.port;
+ }
+ }
+
+ if (this.query &&
+ util.isObject(this.query) &&
+ shims.keys(this.query).length) {
+ query = querystring.stringify(this.query);
+ }
+
+ var search = this.search || (query && ('?' + query)) || '';
+
+ if (protocol && shims.substr(protocol, -1) !== ':') protocol += ':';
+
+ // only the slashedProtocols get the //. Not mailto:, xmpp:, etc.
+ // unless they had them to begin with.
+ if (this.slashes ||
+ (!protocol || slashedProtocol[protocol]) && host !== false) {
+ host = '//' + (host || '');
+ if (pathname && pathname.charAt(0) !== '/') pathname = '/' + pathname;
+ } else if (!host) {
+ host = '';
+ }
+
+ if (hash && hash.charAt(0) !== '#') hash = '#' + hash;
+ if (search && search.charAt(0) !== '?') search = '?' + search;
+
+ pathname = pathname.replace(/[?#]/g, function(match) {
+ return encodeURIComponent(match);
+ });
+ search = search.replace('#', '%23');
+
+ return protocol + host + pathname + search + hash;
+};
+
+function urlResolve(source, relative) {
+ return urlParse(source, false, true).resolve(relative);
+}
+
+Url.prototype.resolve = function(relative) {
+ return this.resolveObject(urlParse(relative, false, true)).format();
+};
+
+function urlResolveObject(source, relative) {
+ if (!source) return relative;
+ return urlParse(source, false, true).resolveObject(relative);
+}
+
+Url.prototype.resolveObject = function(relative) {
+ if (util.isString(relative)) {
+ var rel = new Url();
+ rel.parse(relative, false, true);
+ relative = rel;
+ }
+
+ var result = new Url();
+ shims.forEach(shims.keys(this), function(k) {
+ result[k] = this[k];
+ }, this);
+
+ // hash is always overridden, no matter what.
+ // even href="" will remove it.
+ result.hash = relative.hash;
+
+ // if the relative url is empty, then there's nothing left to do here.
+ if (relative.href === '') {
+ result.href = result.format();
+ return result;
+ }
+
+ // hrefs like //foo/bar always cut to the protocol.
+ if (relative.slashes && !relative.protocol) {
+ // take everything except the protocol from relative
+ shims.forEach(shims.keys(relative), function(k) {
+ if (k !== 'protocol')
+ result[k] = relative[k];
+ });
+
+ //urlParse appends trailing / to urls like http://www.example.com
+ if (slashedProtocol[result.protocol] &&
+ result.hostname && !result.pathname) {
+ result.path = result.pathname = '/';
+ }
+
+ result.href = result.format();
+ return result;
+ }
+
+ if (relative.protocol && relative.protocol !== result.protocol) {
+ // if it's a known url protocol, then changing
+ // the protocol does weird things
+ // first, if it's not file:, then we MUST have a host,
+ // and if there was a path
+ // to begin with, then we MUST have a path.
+ // if it is file:, then the host is dropped,
+ // because that's known to be hostless.
+ // anything else is assumed to be absolute.
+ if (!slashedProtocol[relative.protocol]) {
+ shims.forEach(shims.keys(relative), function(k) {
+ result[k] = relative[k];
+ });
+ result.href = result.format();
+ return result;
+ }
+
+ result.protocol = relative.protocol;
+ if (!relative.host && !hostlessProtocol[relative.protocol]) {
+ var relPath = (relative.pathname || '').split('/');
+ while (relPath.length && !(relative.host = relPath.shift()));
+ if (!relative.host) relative.host = '';
+ if (!relative.hostname) relative.hostname = '';
+ if (relPath[0] !== '') relPath.unshift('');
+ if (relPath.length < 2) relPath.unshift('');
+ result.pathname = relPath.join('/');
+ } else {
+ result.pathname = relative.pathname;
+ }
+ result.search = relative.search;
+ result.query = relative.query;
+ result.host = relative.host || '';
+ result.auth = relative.auth;
+ result.hostname = relative.hostname || relative.host;
+ result.port = relative.port;
+ // to support http.request
+ if (result.pathname || result.search) {
+ var p = result.pathname || '';
+ var s = result.search || '';
+ result.path = p + s;
+ }
+ result.slashes = result.slashes || relative.slashes;
+ result.href = result.format();
+ return result;
+ }
+
+ var isSourceAbs = (result.pathname && result.pathname.charAt(0) === '/'),
+ isRelAbs = (
+ relative.host ||
+ relative.pathname && relative.pathname.charAt(0) === '/'
+ ),
+ mustEndAbs = (isRelAbs || isSourceAbs ||
+ (result.host && relative.pathname)),
+ removeAllDots = mustEndAbs,
+ srcPath = result.pathname && result.pathname.split('/') || [],
+ relPath = relative.pathname && relative.pathname.split('/') || [],
+ psychotic = result.protocol && !slashedProtocol[result.protocol];
+
+ // if the url is a non-slashed url, then relative
+ // links like ../.. should be able
+ // to crawl up to the hostname, as well. This is strange.
+ // result.protocol has already been set by now.
+ // Later on, put the first path part into the host field.
+ if (psychotic) {
+ result.hostname = '';
+ result.port = null;
+ if (result.host) {
+ if (srcPath[0] === '') srcPath[0] = result.host;
+ else srcPath.unshift(result.host);
+ }
+ result.host = '';
+ if (relative.protocol) {
+ relative.hostname = null;
+ relative.port = null;
+ if (relative.host) {
+ if (relPath[0] === '') relPath[0] = relative.host;
+ else relPath.unshift(relative.host);
+ }
+ relative.host = null;
+ }
+ mustEndAbs = mustEndAbs && (relPath[0] === '' || srcPath[0] === '');
+ }
+
+ if (isRelAbs) {
+ // it's absolute.
+ result.host = (relative.host || relative.host === '') ?
+ relative.host : result.host;
+ result.hostname = (relative.hostname || relative.hostname === '') ?
+ relative.hostname : result.hostname;
+ result.search = relative.search;
+ result.query = relative.query;
+ srcPath = relPath;
+ // fall through to the dot-handling below.
+ } else if (relPath.length) {
+ // it's relative
+ // throw away the existing file, and take the new path instead.
+ if (!srcPath) srcPath = [];
+ srcPath.pop();
+ srcPath = srcPath.concat(relPath);
+ result.search = relative.search;
+ result.query = relative.query;
+ } else if (!util.isNullOrUndefined(relative.search)) {
+ // just pull out the search.
+ // like href='?foo'.
+ // Put this after the other two cases because it simplifies the booleans
+ if (psychotic) {
+ result.hostname = result.host = srcPath.shift();
+ //occationaly the auth can get stuck only in host
+ //this especialy happens in cases like
+ //url.resolveObject('mailto:local1@domain1', 'local2@domain2')
+ var authInHost = result.host && result.host.indexOf('@') > 0 ?
+ result.host.split('@') : false;
+ if (authInHost) {
+ result.auth = authInHost.shift();
+ result.host = result.hostname = authInHost.shift();
+ }
+ }
+ result.search = relative.search;
+ result.query = relative.query;
+ //to support http.request
+ if (!util.isNull(result.pathname) || !util.isNull(result.search)) {
+ result.path = (result.pathname ? result.pathname : '') +
+ (result.search ? result.search : '');
+ }
+ result.href = result.format();
+ return result;
+ }
+
+ if (!srcPath.length) {
+ // no path at all. easy.
+ // we've already handled the other stuff above.
+ result.pathname = null;
+ //to support http.request
+ if (result.search) {
+ result.path = '/' + result.search;
+ } else {
+ result.path = null;
+ }
+ result.href = result.format();
+ return result;
+ }
+
+ // if a url ENDs in . or .., then it must get a trailing slash.
+ // however, if it ends in anything else non-slashy,
+ // then it must NOT get a trailing slash.
+ var last = srcPath.slice(-1)[0];
+ var hasTrailingSlash = (
+ (result.host || relative.host) && (last === '.' || last === '..') ||
+ last === '');
+
+ // strip single dots, resolve double dots to parent dir
+ // if the path tries to go above the root, `up` ends up > 0
+ var up = 0;
+ for (var i = srcPath.length; i >= 0; i--) {
+ last = srcPath[i];
+ if (last == '.') {
+ srcPath.splice(i, 1);
+ } else if (last === '..') {
+ srcPath.splice(i, 1);
+ up++;
+ } else if (up) {
+ srcPath.splice(i, 1);
+ up--;
+ }
+ }
+
+ // if the path is allowed to go above the root, restore leading ..s
+ if (!mustEndAbs && !removeAllDots) {
+ for (; up--; up) {
+ srcPath.unshift('..');
+ }
+ }
+
+ if (mustEndAbs && srcPath[0] !== '' &&
+ (!srcPath[0] || srcPath[0].charAt(0) !== '/')) {
+ srcPath.unshift('');
+ }
+
+ if (hasTrailingSlash && (shims.substr(srcPath.join('/'), -1) !== '/')) {
+ srcPath.push('');
+ }
+
+ var isAbsolute = srcPath[0] === '' ||
+ (srcPath[0] && srcPath[0].charAt(0) === '/');
+
+ // put the host back
+ if (psychotic) {
+ result.hostname = result.host = isAbsolute ? '' :
+ srcPath.length ? srcPath.shift() : '';
+ //occationaly the auth can get stuck only in host
+ //this especialy happens in cases like
+ //url.resolveObject('mailto:local1@domain1', 'local2@domain2')
+ var authInHost = result.host && result.host.indexOf('@') > 0 ?
+ result.host.split('@') : false;
+ if (authInHost) {
+ result.auth = authInHost.shift();
+ result.host = result.hostname = authInHost.shift();
+ }
+ }
+
+ mustEndAbs = mustEndAbs || (result.host && srcPath.length);
+
+ if (mustEndAbs && !isAbsolute) {
+ srcPath.unshift('');
+ }
+
+ if (!srcPath.length) {
+ result.pathname = null;
+ result.path = null;
+ } else {
+ result.pathname = srcPath.join('/');
+ }
+
+ //to support request.http
+ if (!util.isNull(result.pathname) || !util.isNull(result.search)) {
+ result.path = (result.pathname ? result.pathname : '') +
+ (result.search ? result.search : '');
+ }
+ result.auth = relative.auth || result.auth;
+ result.slashes = result.slashes || relative.slashes;
+ result.href = result.format();
+ return result;
+};
+
+Url.prototype.parseHost = function() {
+ var host = this.host;
+ var port = portPattern.exec(host);
+ if (port) {
+ port = port[0];
+ if (port !== ':') {
+ this.port = port.substr(1);
+ }
+ host = host.substr(0, host.length - port.length);
+ }
+ if (host) this.hostname = host;
+};
+
+});
+
+;require.register("util", function(exports, require, module) {
+// Copyright Joyent, Inc. and other Node contributors.
+//
+// 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.
+
+var shims = require('_shims');
+
+// NOTE: These type checking functions intentionally don't use `instanceof`
+// because it is fragile and can be easily faked with `Object.create()`.
+function isArray(ar) {
+return shims.isArray(ar);
+}
+exports.isArray = isArray;
+
+function isBoolean(arg) {
+return typeof arg === 'boolean';
+}
+exports.isBoolean = isBoolean;
+
+function isNull(arg) {
+return arg === null;
+}
+exports.isNull = isNull;
+
+function isNullOrUndefined(arg) {
+return arg == null;
+}
+exports.isNullOrUndefined = isNullOrUndefined;
+
+function isNumber(arg) {
+return typeof arg === 'number';
+}
+exports.isNumber = isNumber;
+
+function isString(arg) {
+return typeof arg === 'string';
+}
+exports.isString = isString;
+
+function isObject(arg) {
+return typeof arg === 'object' && arg;
+}
+exports.isObject = isObject;
+
+function objectToString(o) {
+return Object.prototype.toString.call(o);
+}
+
+});
+
+;require.register("backbone-orm/lib/cache/cursor", function(exports, require, module) {
+/*
+ backbone-orm.js 0.5.3
+ Copyright (c) 2013 Vidigami - https://github.com/vidigami/backbone-orm
+ License: MIT (http://www.opensource.org/licenses/mit-license.php)
+ Dependencies: Backbone.js, Underscore.js, Moment.js, and Inflection.js.
+*/
+
+var CacheCursor, _, _ref,
+ __hasProp = {}.hasOwnProperty,
+ __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
+
+_ = require('underscore');
+
+module.exports = CacheCursor = (function(_super) {
+ __extends(CacheCursor, _super);
+
+ function CacheCursor() {
+ _ref = CacheCursor.__super__.constructor.apply(this, arguments);
+ return _ref;
+ }
+
+ CacheCursor.prototype.toJSON = function(callback) {
+ return this.wrapped_sync_fn('cursor', _.extend({}, this._find, this._cursor)).toJSON(callback);
+ };
+
+ return CacheCursor;
+
+})(require('../cursor'));
+
+});
+
+;require.register("backbone-orm/lib/cache/memory_store", function(exports, require, module) {
+/*
+ backbone-orm.js 0.5.3
+ Copyright (c) 2013 Vidigami - https://github.com/vidigami/backbone-orm
+ License: MIT (http://www.opensource.org/licenses/mit-license.php)
+ Dependencies: Backbone.js, Underscore.js, Moment.js, and Inflection.js.
+*/
+
+var LRU, MemoryStore, inflection, _,
+ __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
+
+_ = require('underscore');
+
+LRU = require('lru-cache');
+
+inflection = require('inflection');
+
+module.exports = MemoryStore = (function() {
+ function MemoryStore(options) {
+ var key, normalized_options, value;
+ if (options == null) {
+ options = {};
+ }
+ this.forEach = __bind(this.forEach, this);
+ this.reset = __bind(this.reset, this);
+ this.destroy = __bind(this.destroy, this);
+ this.get = __bind(this.get, this);
+ this.set = __bind(this.set, this);
+ normalized_options = {};
+ for (key in options) {
+ value = options[key];
+ if (key === 'destroy') {
+ normalized_options.dispose = value;
+ } else {
+ normalized_options[this._normalizeKey(key)] = value;
+ }
+ }
+ this.cache = new LRU(normalized_options);
+ }
+
+ MemoryStore.prototype.set = function(key, value, callback) {
+ if (value._orm_never_cache) {
+ return (typeof callback === "function" ? callback(null, value) : void 0) || this;
+ }
+ this.cache.set(key, value);
+ if (typeof callback === "function") {
+ callback(null, value);
+ }
+ return this;
+ };
+
+ MemoryStore.prototype.get = function(key, callback) {
+ var value;
+ value = this.cache.get(key);
+ if (typeof callback === "function") {
+ callback(null, value);
+ }
+ return value;
+ };
+
+ MemoryStore.prototype.destroy = function(key, callback) {
+ this.cache.del(key);
+ if (typeof callback === "function") {
+ callback();
+ }
+ return this;
+ };
+
+ MemoryStore.prototype.reset = function(callback) {
+ this.cache.reset();
+ if (typeof callback === "function") {
+ callback();
+ }
+ return this;
+ };
+
+ MemoryStore.prototype._normalizeKey = function(key) {
+ key = inflection.underscore(key);
+ if (key.indexOf('_') < 0) {
+ return key.toLowerCase();
+ }
+ return inflection.camelize(key);
+ };
+
+ MemoryStore.prototype.forEach = function(callback) {
+ return this.cache.forEach(callback);
+ };
+
+ return MemoryStore;
+
+})();
+
+});
+
+;require.register("backbone-orm/lib/cache/model_cache", function(exports, require, module) {
+/*
+ backbone-orm.js 0.5.3
+ Copyright (c) 2013 Vidigami - https://github.com/vidigami/backbone-orm
+ License: MIT (http://www.opensource.org/licenses/mit-license.php)
+ Dependencies: Backbone.js, Underscore.js, Moment.js, and Inflection.js.
+*/
+
+var Backbone, MEMORY_STORE_KEYS, MemoryStore, ModelCache, Queue, _;
+
+Backbone = require('backbone');
+
+_ = require('underscore');
+
+Queue = require('../queue');
+
+MemoryStore = require('./memory_store');
+
+MEMORY_STORE_KEYS = ['max', 'max_age', 'destroy'];
+
+module.exports = ModelCache = (function() {
+ function ModelCache() {
+ this.enabled = false;
+ this.caches = {};
+ this.options = {
+ modelTypes: {}
+ };
+ this.verbose = false;
+ }
+
+ ModelCache.prototype.configure = function(options) {
+ var key, value, value_key, value_value, values, _base;
+ if (options == null) {
+ options = {};
+ }
+ this.enabled = options.enabled;
+ this.reset(function() {});
+ for (key in options) {
+ value = options[key];
+ if (_.isObject(value)) {
+ (_base = this.options)[key] || (_base[key] = {});
+ values = this.options[key];
+ for (value_key in value) {
+ value_value = value[value_key];
+ values[value_key] = value_value;
+ }
+ } else {
+ this.options[key] = value;
+ }
+ }
+ return this;
+ };
+
+ ModelCache.prototype.configureSync = function(model_type, sync_fn) {
+ var cache;
+ if (model_type.prototype._orm_never_cache || !(cache = this.getOrCreateCache(model_type.model_name))) {
+ return sync_fn;
+ }
+ model_type.cache = cache;
+ return require('./sync')(model_type, sync_fn);
+ };
+
+ ModelCache.prototype.reset = function(callback) {
+ var key, queue, value, _fn, _ref;
+ queue = new Queue();
+ _ref = this.caches;
+ _fn = function(value) {
+ return queue.defer(function(callback) {
+ return value.reset(callback);
+ });
+ };
+ for (key in _ref) {
+ value = _ref[key];
+ _fn(value);
+ }
+ return queue.await(callback);
+ };
+
+ ModelCache.prototype.hardReset = function() {
+ var key, value, _ref;
+ this.reset(function() {});
+ _ref = this.caches;
+ for (key in _ref) {
+ value = _ref[key];
+ delete this.caches[key];
+ }
+ return this;
+ };
+
+ ModelCache.prototype.getOrCreateCache = function(model_name) {
+ var model_cache, options, _base;
+ if (!this.enabled) {
+ return null;
+ }
+ if (!model_name) {
+ throw new Error("Missing model name for cache");
+ }
+ if (model_cache = this.caches[model_name]) {
+ return model_cache;
+ }
+ if (options = this.options.modelTypes[model_name]) {
+ return this.caches[model_name] = (typeof options.store === "function" ? options.store() : void 0) || new MemoryStore(_.pick(options, MEMORY_STORE_KEYS));
+ } else if (this.options.store || this.options.max || this.options.max_age) {
+ return this.caches[model_name] = (typeof (_base = this.options).store === "function" ? _base.store() : void 0) || new MemoryStore(_.pick(this.options, MEMORY_STORE_KEYS));
+ }
+ return null;
+ };
+
+ return ModelCache;
+
+})();
+
+});
+
+;require.register("backbone-orm/lib/cache/query_cache", function(exports, require, module) {
+/*
+ backbone-orm.js 0.5.3
+ Copyright (c) 2013 Vidigami - https://github.com/vidigami/backbone-orm
+ License: MIT (http://www.opensource.org/licenses/mit-license.php)
+ Dependencies: Backbone.js, Underscore.js, Moment.js, and Inflection.js.
+*/
+
+var CLONE_DEPTH, JSONUtils, MemoryStore, QueryCache, Queue, inflection, _,
+ __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
+
+_ = require('underscore');
+
+inflection = require('inflection');
+
+Queue = require('../queue');
+
+JSONUtils = require('../json_utils');
+
+MemoryStore = require('./memory_store');
+
+CLONE_DEPTH = 2;
+
+module.exports = QueryCache = (function() {
+ function QueryCache() {
+ this.storeKeyForModelTypes = __bind(this.storeKeyForModelTypes, this);
+ this.getKeysForModelTypes = __bind(this.getKeysForModelTypes, this);
+ this.clearMetaForModelTypes = __bind(this.clearMetaForModelTypes, this);
+ this.clearModelTypes = __bind(this.clearModelTypes, this);
+ this.reset = __bind(this.reset, this);
+ this.hardReset = __bind(this.hardReset, this);
+ this.getMeta = __bind(this.getMeta, this);
+ this.getKey = __bind(this.getKey, this);
+ this.get = __bind(this.get, this);
+ this.set = __bind(this.set, this);
+ this.configure = __bind(this.configure, this);
+ this.enabled = false;
+ }
+
+ QueryCache.prototype.configure = function(options) {
+ if (options == null) {
+ options = {};
+ }
+ this.enabled = options.enabled;
+ this.verbose = options.verbose;
+ this.hits = this.misses = this.clears = 0;
+ this.store = options.store || new MemoryStore();
+ return this;
+ };
+
+ QueryCache.prototype.cacheKey = function(model_type, query) {
+ return "" + model_type.model_id + "_" + (JSON.stringify(query));
+ };
+
+ QueryCache.prototype.cacheKeyMeta = function(model_type) {
+ return "meta_" + model_type.model_id;
+ };
+
+ QueryCache.prototype.set = function(model_type, query, related_model_types, value, callback) {
+ var cache_key, m, model_types,
+ _this = this;
+ if (!this.enabled) {
+ return callback();
+ }
+ if (this.verbose) {
+ console.log('QueryCache:set', model_type.model_name, (function() {
+ var _i, _len, _results;
+ _results = [];
+ for (_i = 0, _len = related_model_types.length; _i < _len; _i++) {
+ m = related_model_types[_i];
+ _results.push(m.model_name);
+ }
+ return _results;
+ })(), this.cacheKey(model_type, query), JSON.stringify(value), '\n-----------');
+ }
+ model_types = [model_type].concat(related_model_types || []);
+ cache_key = this.cacheKey(model_type, query);
+ return this.store.set(cache_key, JSONUtils.deepClone(value, CLONE_DEPTH), function(err) {
+ if (err) {
+ return callback(err);
+ }
+ return _this.storeKeyForModelTypes(model_types, cache_key, callback);
+ });
+ };
+
+ QueryCache.prototype.get = function(model_type, query, callback) {
+ if (!this.enabled) {
+ return callback();
+ }
+ return this.getKey(this.cacheKey(model_type, query), callback);
+ };
+
+ QueryCache.prototype.getKey = function(key, callback) {
+ var _this = this;
+ if (!this.enabled) {
+ return callback();
+ }
+ return this.store.get(key, function(err, value) {
+ if (err) {
+ return callback(err);
+ }
+ if (_.isUndefined(value) || _.isNull(value)) {
+ _this.misses++;
+ if (_this.verbose) {
+ console.log('QueryCache:miss', key, value, '\n-----------');
+ }
+ return callback();
+ } else {
+ _this.hits++;
+ if (_this.verbose) {
+ console.log('QueryCache:hit', key, value, '\n-----------');
+ }
+ return callback(null, JSONUtils.deepClone(value, CLONE_DEPTH));
+ }
+ });
+ };
+
+ QueryCache.prototype.getMeta = function(model_type, callback) {
+ if (!this.enabled) {
+ return callback();
+ }
+ return this.store.get(this.cacheKeyMeta(model_type), callback);
+ };
+
+ QueryCache.prototype.hardReset = function(callback) {
+ if (!this.enabled) {
+ return callback();
+ }
+ if (this.verbose) {
+ console.log('QueryCache:hardReset');
+ }
+ this.hits = this.misses = this.clears = 0;
+ if (this.store) {
+ return this.store.reset(callback);
+ }
+ return callback();
+ };
+
+ QueryCache.prototype.reset = function(model_types, callback) {
+ var model_type, related_model_types, _i, _len;
+ if (arguments.length === 1) {
+ return this.hardReset(model_types);
+ }
+ if (!this.enabled) {
+ return callback();
+ }
+ if (!_.isArray(model_types)) {
+ model_types = [model_types];
+ }
+ related_model_types = [];
+ for (_i = 0, _len = model_types.length; _i < _len; _i++) {
+ model_type = model_types[_i];
+ related_model_types = related_model_types.concat(model_type.schema().allRelations());
+ }
+ model_types = model_types.concat(related_model_types);
+ return this.clearModelTypes(model_types, callback);
+ };
+
+ QueryCache.prototype.clearModelTypes = function(model_types, callback) {
+ var _this = this;
+ if (!model_types.length) {
+ return callback();
+ }
+ return this.getKeysForModelTypes(model_types, function(err, to_clear) {
+ var key, queue, _fn, _i, _len, _ref;
+ if (err) {
+ return callback(err);
+ }
+ queue = new Queue();
+ queue.defer(function(callback) {
+ return _this.clearMetaForModelTypes(model_types, callback);
+ });
+ _ref = _.uniq(to_clear);
+ _fn = function(key) {
+ return queue.defer(function(callback) {
+ if (_this.verbose) {
+ console.log('QueryCache:cleared', key, '\n-----------');
+ }
+ _this.clears++;
+ return _this.store.destroy(key, callback);
+ });
+ };
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ key = _ref[_i];
+ _fn(key);
+ }
+ return queue.await(callback);
+ });
+ };
+
+ QueryCache.prototype.clearMetaForModelTypes = function(model_types, callback) {
+ var model_type, queue, _fn, _i, _len,
+ _this = this;
+ queue = new Queue();
+ _fn = function(model_type) {
+ return queue.defer(function(callback) {
+ if (_this.verbose) {
+ console.log('QueryCache:meta cleared', model_type.model_name, '\n-----------');
+ }
+ return _this.store.destroy(_this.cacheKeyMeta(model_type), callback);
+ });
+ };
+ for (_i = 0, _len = model_types.length; _i < _len; _i++) {
+ model_type = model_types[_i];
+ _fn(model_type);
+ }
+ return queue.await(callback);
+ };
+
+ QueryCache.prototype.getKeysForModelTypes = function(model_types, callback) {
+ var all_keys, model_type, queue, _fn, _i, _len,
+ _this = this;
+ all_keys = [];
+ queue = new Queue(1);
+ _fn = function(model_type) {
+ return queue.defer(function(callback) {
+ return _this.getMeta(model_type, function(err, keys) {
+ if (err || !keys) {
+ return callback(err);
+ }
+ all_keys = all_keys.concat(keys);
+ return callback();
+ });
+ });
+ };
+ for (_i = 0, _len = model_types.length; _i < _len; _i++) {
+ model_type = model_types[_i];
+ _fn(model_type);
+ }
+ return queue.await(function(err) {
+ return callback(err, all_keys);
+ });
+ };
+
+ QueryCache.prototype.storeKeyForModelTypes = function(model_types, cache_key, callback) {
+ var model_type, queue, _fn, _i, _len,
+ _this = this;
+ queue = new Queue(1);
+ _fn = function(model_type) {
+ return queue.defer(function(callback) {
+ var model_type_key;
+ model_type_key = _this.cacheKeyMeta(model_type);
+ return _this.store.get(model_type_key, function(err, keys) {
+ if (err) {
+ return callback(err);
+ }
+ (keys || (keys = [])).push(cache_key);
+ return _this.store.set(model_type_key, _.uniq(keys), callback);
+ });
+ });
+ };
+ for (_i = 0, _len = model_types.length; _i < _len; _i++) {
+ model_type = model_types[_i];
+ _fn(model_type);
+ }
+ return queue.await(callback);
+ };
+
+ return QueryCache;
+
+})();
+
+});
+
+;require.register("backbone-orm/lib/cache/singletons", function(exports, require, module) {
+/*
+ backbone-orm.js 0.5.3
+ Copyright (c) 2013 Vidigami - https://github.com/vidigami/backbone-orm
+ License: MIT (http://www.opensource.org/licenses/mit-license.php)
+ Dependencies: Backbone.js, Underscore.js, Moment.js, and Inflection.js.
+*/
+
+var e;
+
+module.exports = {
+ ModelCache: new (require('./model_cache'))(),
+ QueryCache: new (require('./query_cache'))()
+};
+
+try {
+ module.exports.ModelTypeID = new (require('../node/model_type_id'))();
+} catch (_error) {
+ e = _error;
+}
+
+});
+
+;require.register("backbone-orm/lib/cache/sync", function(exports, require, module) {
+/*
+ backbone-orm.js 0.5.3
+ Copyright (c) 2013 Vidigami - https://github.com/vidigami/backbone-orm
+ License: MIT (http://www.opensource.org/licenses/mit-license.php)
+ Dependencies: Backbone.js, Underscore.js, Moment.js, and Inflection.js.
+*/
+
+var CacheCursor, CacheSync, DESTROY_BATCH_LIMIT, DESTROY_THREADS, Schema, Utils, bbCallback, _;
+
+_ = require('underscore');
+
+CacheCursor = require('./cursor');
+
+Schema = require('../schema');
+
+Utils = require('../utils');
+
+bbCallback = Utils.bbCallback;
+
+DESTROY_BATCH_LIMIT = 1000;
+
+DESTROY_THREADS = 100;
+
+CacheSync = (function() {
+ function CacheSync(model_type, wrapped_sync_fn) {
+ this.model_type = model_type;
+ this.wrapped_sync_fn = wrapped_sync_fn;
+ }
+
+ CacheSync.prototype.initialize = function() {
+ if (this.is_initialized) {
+ return;
+ }
+ this.is_initialized = true;
+ this.wrapped_sync_fn('initialize');
+ if (!this.model_type.model_name) {
+ throw new Error('Missing model_name for model');
+ }
+ };
+
+ CacheSync.prototype.read = function(model, options) {
+ var cached_model;
+ if (!options.force && (cached_model = this.model_type.cache.get(model.id))) {
+ return options.success(cached_model.toJSON());
+ }
+ return this.wrapped_sync_fn('read', model, options);
+ };
+
+ CacheSync.prototype.create = function(model, options) {
+ var _this = this;
+ return this.wrapped_sync_fn('create', model, bbCallback(function(err, json) {
+ var attributes, cache_model;
+ if (err) {
+ return options.error(err);
+ }
+ (attributes = {})[_this.model_type.prototype.idAttribute] = json[_this.model_type.prototype.idAttribute];
+ model.set(attributes);
+ if (cache_model = _this.model_type.cache.get(model.id)) {
+ if (cache_model !== model) {
+ Utils.updateModel(cache_model, model);
+ }
+ } else {
+ _this.model_type.cache.set(model.id, model);
+ }
+ return options.success(json);
+ }));
+ };
+
+ CacheSync.prototype.update = function(model, options) {
+ var _this = this;
+ return this.wrapped_sync_fn('update', model, bbCallback(function(err, json) {
+ var cache_model;
+ if (err) {
+ return options.error(err);
+ }
+ if (cache_model = _this.model_type.cache.get(model.id)) {
+ if (cache_model !== model) {
+ Utils.updateModel(cache_model, model);
+ }
+ } else {
+ _this.model_type.cache.set(model.id, model);
+ }
+ return options.success(json);
+ }));
+ };
+
+ CacheSync.prototype["delete"] = function(model, options) {
+ var _this = this;
+ this.model_type.cache.destroy(model.id);
+ return this.wrapped_sync_fn('delete', model, bbCallback(function(err, json) {
+ if (err) {
+ return options.error(err);
+ }
+ return options.success(json);
+ }));
+ };
+
+ CacheSync.prototype.resetSchema = function(options, callback) {
+ var _this = this;
+ return this.model_type.cache.reset(function(err) {
+ if (err) {
+ return callback(err);
+ }
+ return _this.wrapped_sync_fn('resetSchema', options, callback);
+ });
+ };
+
+ CacheSync.prototype.cursor = function(query) {
+ if (query == null) {
+ query = {};
+ }
+ return new CacheCursor(query, _.pick(this, ['model_type', 'wrapped_sync_fn']));
+ };
+
+ CacheSync.prototype.destroy = function(query, callback) {
+ var _this = this;
+ return this.model_type.each(_.extend({
+ $each: {
+ limit: DESTROY_BATCH_LIMIT,
+ threads: DESTROY_THREADS
+ }
+ }, query), (function(model, callback) {
+ return model.destroy(callback);
+ }), callback);
+ };
+
+ CacheSync.prototype.connect = function(url) {
+ this.model_type.cache.reset();
+ return this.wrapped_sync_fn('connect');
+ };
+
+ return CacheSync;
+
+})();
+
+module.exports = function(model_type, wrapped_sync_fn) {
+ var sync, sync_fn;
+ sync = new CacheSync(model_type, wrapped_sync_fn);
+ model_type.prototype.sync = sync_fn = function(method, model, options) {
+ if (options == null) {
+ options = {};
+ }
+ sync.initialize();
+ if (method === 'createSync') {
+ return wrapped_sync_fn.apply(null, arguments);
+ }
+ if (method === 'sync') {
+ return sync;
+ }
+ if (sync[method]) {
+ return sync[method].apply(sync, Array.prototype.slice.call(arguments, 1));
+ }
+ return wrapped_sync_fn.apply(wrapped_sync_fn, Array.prototype.slice.call(arguments));
+ };
+ return sync_fn;
+};
+
+});
+
+;require.register("backbone-orm/lib/connection_pool", function(exports, require, module) {
+/*
+ backbone-orm.js 0.5.3
+ Copyright (c) 2013 Vidigami - https://github.com/vidigami/backbone-orm
+ License: MIT (http://www.opensource.org/licenses/mit-license.php)
+ Dependencies: Backbone.js, Underscore.js, Moment.js, and Inflection.js.
+*/
+
+var MemoryStore;
+
+MemoryStore = require('./cache/memory_store');
+
+module.exports = new MemoryStore({
+ destroy: function(url, connection) {
+ return connection.destroy();
+ }
+});
+
+});
+
+;require.register("backbone-orm/lib/cursor", function(exports, require, module) {
+/*
+ backbone-orm.js 0.5.3
+ Copyright (c) 2013 Vidigami - https://github.com/vidigami/backbone-orm
+ License: MIT (http://www.opensource.org/licenses/mit-license.php)
+ Dependencies: Backbone.js, Underscore.js, Moment.js, and Inflection.js.
+*/
+
+var CURSOR_KEYS, Cursor, QueryCache, Utils, _,
+ __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
+
+_ = require('underscore');
+
+QueryCache = require('./cache/singletons').QueryCache;
+
+Utils = require('./utils');
+
+CURSOR_KEYS = ['$count', '$exists', '$zero', '$one', '$offset', '$limit', '$page', '$sort', '$white_list', '$select', '$include', '$values', '$ids'];
+
+module.exports = Cursor = (function() {
+ var _this = this;
+
+ function Cursor(query, options) {
+ this.relatedModelTypesInQuery = __bind(this.relatedModelTypesInQuery, this);
+ var key, parsed_query, value, _i, _len, _ref;
+ for (key in options) {
+ value = options[key];
+ this[key] = value;
+ }
+ parsed_query = Cursor.parseQuery(query, this.model_type);
+ this._find = parsed_query.find;
+ this._cursor = parsed_query.cursor;
+ _ref = ['$white_list', '$select', '$values'];
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ key = _ref[_i];
+ if (this._cursor[key] && !_.isArray(this._cursor[key])) {
+ this._cursor[key] = [this._cursor[key]];
+ }
+ }
+ }
+
+ Cursor.validateQuery = function(query, memo, model_type) {
+ var full_key, key, value, _results;
+ _results = [];
+ for (key in query) {
+ value = query[key];
+ if (!(_.isUndefined(value) || _.isObject(value))) {
+ continue;
+ }
+ full_key = memo ? "" + memo + "." + key : key;
+ if (_.isUndefined(value)) {
+ throw new Error("Unexpected undefined for query key '" + full_key + "' on " + (model_type != null ? model_type.model_name : void 0));
+ }
+ if (_.isObject(value)) {
+ _results.push(Cursor.validateQuery(value, full_key, model_type));
+ } else {
+ _results.push(void 0);
+ }
+ }
+ return _results;
+ };
+
+ Cursor.parseQuery = function(query, model_type) {
+ var e, key, parsed_query, value;
+ if (!query) {
+ return {
+ find: {},
+ cursor: {}
+ };
+ } else if (!_.isObject(query)) {
+ return {
+ find: {
+ id: query
+ },
+ cursor: {
+ $one: true
+ }
+ };
+ } else if (query.find || query.cursor) {
+ return {
+ find: query.find || {},
+ cursor: query.cursor || {}
+ };
+ } else {
+ try {
+ Cursor.validateQuery(query, null, model_type);
+ } catch (_error) {
+ e = _error;
+ throw new Error("Error: " + e + ". Query: ", query);
+ }
+ parsed_query = {
+ find: {},
+ cursor: {}
+ };
+ for (key in query) {
+ value = query[key];
+ if (key[0] !== '$') {
+ parsed_query.find[key] = value;
+ } else {
+ parsed_query.cursor[key] = value;
+ }
+ }
+ return parsed_query;
+ }
+ };
+
+ Cursor.prototype.offset = function(offset) {
+ this._cursor.$offset = offset;
+ return this;
+ };
+
+ Cursor.prototype.limit = function(limit) {
+ this._cursor.$limit = limit;
+ return this;
+ };
+
+ Cursor.prototype.sort = function(sort) {
+ this._cursor.$sort = sort;
+ return this;
+ };
+
+ Cursor.prototype.whiteList = function(args) {
+ var keys;
+ keys = _.flatten(arguments);
+ this._cursor.$white_list = this._cursor.$white_list ? _.intersection(this._cursor.$white_list, keys) : keys;
+ return this;
+ };
+
+ Cursor.prototype.select = function(args) {
+ var keys;
+ keys = _.flatten(arguments);
+ this._cursor.$select = this._cursor.$select ? _.intersection(this._cursor.$select, keys) : keys;
+ return this;
+ };
+
+ Cursor.prototype.include = function(args) {
+ var keys;
+ keys = _.flatten(arguments);
+ this._cursor.$include = this._cursor.$include ? _.intersection(this._cursor.$include, keys) : keys;
+ return this;
+ };
+
+ Cursor.prototype.values = function(args) {
+ var keys;
+ keys = _.flatten(arguments);
+ this._cursor.$values = this._cursor.$values ? _.intersection(this._cursor.$values, keys) : keys;
+ return this;
+ };
+
+ Cursor.prototype.ids = function() {
+ this._cursor.$values = ['id'];
+ return this;
+ };
+
+ Cursor.prototype.count = function(callback) {
+ return this.execWithCursorQuery('$count', 'toJSON', callback);
+ };
+
+ Cursor.prototype.exists = function(callback) {
+ return this.execWithCursorQuery('$exists', 'toJSON', callback);
+ };
+
+ Cursor.prototype.toModel = function(callback) {
+ return this.execWithCursorQuery('$one', 'toModels', callback);
+ };
+
+ Cursor.prototype.toModels = function(callback) {
+ var _this = this;
+ if (this._cursor.$values) {
+ return callback(new Error("Cannot call toModels on cursor with values for model " + this.model_type.model_name + ". Values: " + (Utils.inspect(this._cursor.$values))));
+ }
+ return this.toJSON(function(err, json) {
+ if (err) {
+ return callback(err);
+ }
+ if (_this._cursor.$one && !json) {
+ return callback(null, null);
+ }
+ if (!_.isArray(json)) {
+ json = [json];
+ }
+ return _this.prepareIncludes(json, function(err, json) {
+ var can_cache, item, model, models;
+ if (can_cache = !(_this._cursor.$select || _this._cursor.$whitelist)) {
+ models = (function() {
+ var _i, _len, _results;
+ _results = [];
+ for (_i = 0, _len = json.length; _i < _len; _i++) {
+ item = json[_i];
+ _results.push(Utils.updateOrNew(item, this.model_type));
+ }
+ return _results;
+ }).call(_this);
+ } else {
+ models = ((function() {
+ var _i, _len, _results;
+ model = new this.model_type(this.model_type.prototype.parse(item));
+ model.setPartial(true);
+ _results = [];
+ for (_i = 0, _len = json.length; _i < _len; _i++) {
+ item = json[_i];
+ _results.push(model);
+ }
+ return _results;
+ }).call(_this));
+ }
+ return callback(null, _this._cursor.$one ? models[0] : models);
+ });
+ });
+ };
+
+ Cursor.prototype.toJSON = function(callback) {
+ var parsed_query,
+ _this = this;
+ parsed_query = _.extend({}, _.pick(this._cursor, CURSOR_KEYS), this._find);
+ return QueryCache.get(this.model_type, parsed_query, function(err, cached_result) {
+ var model_types;
+ if (err) {
+ return callback(err);
+ }
+ if (!_.isUndefined(cached_result)) {
+ return callback(null, cached_result);
+ }
+ model_types = _this.relatedModelTypesInQuery();
+ return _this.queryToJSON(function(err, json) {
+ if (err) {
+ return callback(err);
+ }
+ if (!_.isNull(json)) {
+ return QueryCache.set(_this.model_type, parsed_query, model_types, json, function(err) {
+ if (err) {
+ console.log("Error setting query cache: " + err);
+ }
+ return callback(null, json);
+ });
+ } else {
+ return callback(null, json);
+ }
+ });
+ });
+ };
+
+ Cursor.prototype.queryToJSON = function(callback) {
+ throw new Error('toJSON must be implemented by a concrete cursor for a Backbone Sync type');
+ };
+
+ Cursor.prototype.hasCursorQuery = function(key) {
+ return this._cursor[key] || (this._cursor[key] === '');
+ };
+
+ Cursor.prototype.execWithCursorQuery = function(key, method, callback) {
+ var value,
+ _this = this;
+ value = this._cursor[key];
+ this._cursor[key] = true;
+ return this[method](function(err, json) {
+ if (_.isUndefined(value)) {
+ delete _this._cursor[key];
+ } else {
+ _this._cursor[key] = value;
+ }
+ return callback(err, json);
+ });
+ };
+
+ Cursor.prototype.relatedModelTypesInQuery = function() {
+ var key, related_fields, related_model_types, relation, relation_key, reverse_relation, value, _i, _len, _ref, _ref1, _ref2;
+ related_fields = [];
+ related_model_types = [];
+ _ref = this._find;
+ for (key in _ref) {
+ value = _ref[key];
+ if (key.indexOf('.') > 0) {
+ _ref1 = key.split('.'), relation_key = _ref1[0], key = _ref1[1];
+ related_fields.push(relation_key);
+ } else if ((reverse_relation = this.model_type.reverseRelation(key)) && reverse_relation.join_table) {
+ related_model_types.push(reverse_relation.model_type);
+ related_model_types.push(reverse_relation.join_table);
+ }
+ }
+ if ((_ref2 = this._cursor) != null ? _ref2.$include : void 0) {
+ related_fields = related_fields.concat(this._cursor.$include);
+ }
+ for (_i = 0, _len = related_fields.length; _i < _len; _i++) {
+ relation_key = related_fields[_i];
+ if (relation = this.model_type.relation(relation_key)) {
+ related_model_types.push(relation.reverse_model_type);
+ if (relation.join_table) {
+ related_model_types.push(relation.join_table);
+ }
+ }
+ }
+ return related_model_types;
+ };
+
+ Cursor.prototype.selectResults = function(json) {
+ var $select, $values, item, key;
+ if (this._cursor.$values) {
+ $values = this._cursor.$white_list ? _.intersection(this._cursor.$values, this._cursor.$white_list) : this._cursor.$values;
+ if (this._cursor.$values.length === 1) {
+ key = this._cursor.$values[0];
+ json = $values.length ? (function() {
+ var _i, _len, _results;
+ _results = [];
+ for (_i = 0, _len = json.length; _i < _len; _i++) {
+ item = json[_i];
+ _results.push(item.hasOwnProperty(key) ? item[key] : null);
+ }
+ return _results;
+ })() : _.map(json, function() {
+ return null;
+ });
+ } else {
+ json = (function() {
+ var _i, _len, _results;
+ _results = [];
+ for (_i = 0, _len = json.length; _i < _len; _i++) {
+ item = json[_i];
+ _results.push((function() {
+ var _j, _len1, _results1;
+ _results1 = [];
+ for (_j = 0, _len1 = $values.length; _j < _len1; _j++) {
+ key = $values[_j];
+ if (item.hasOwnProperty(key)) {
+ _results1.push(item[key]);
+ }
+ }
+ return _results1;
+ })());
+ }
+ return _results;
+ })();
+ }
+ } else if (this._cursor.$select) {
+ $select = this._cursor.$white_list ? _.intersection(this._cursor.$select, this._cursor.$white_list) : this._cursor.$select;
+ json = (function() {
+ var _i, _len, _results;
+ _results = [];
+ for (_i = 0, _len = json.length; _i < _len; _i++) {
+ item = json[_i];
+ _results.push(_.pick(item, $select));
+ }
+ return _results;
+ })();
+ } else if (this._cursor.$white_list) {
+ json = (function() {
+ var _i, _len, _results;
+ _results = [];
+ for (_i = 0, _len = json.length; _i < _len; _i++) {
+ item = json[_i];
+ _results.push(_.pick(item, this._cursor.$white_list));
+ }
+ return _results;
+ }).call(this);
+ }
+ if (this._cursor.$one) {
+ return json[0] || null;
+ } else {
+ return json;
+ }
+ };
+
+ Cursor.prototype.selectFromModels = function(models, callback) {
+ var $select, item, model;
+ if (this._cursor.$select) {
+ $select = this._cursor.$white_list ? _.intersection(this._cursor.$select, this._cursor.$white_list) : this._cursor.$select;
+ models = ((function() {
+ var _i, _len, _results;
+ model = new this.model_type(_.pick(model.attributes, $select));
+ model.setPartial(true);
+ _results = [];
+ for (_i = 0, _len = models.length; _i < _len; _i++) {
+ item = models[_i];
+ _results.push(model);
+ }
+ return _results;
+ }).call(this));
+ } else if (this._cursor.$white_list) {
+ models = ((function() {
+ var _i, _len, _results;
+ model = new this.model_type(_.pick(model.attributes, this._cursor.$white_list));
+ model.setPartial(true);
+ _results = [];
+ for (_i = 0, _len = models.length; _i < _len; _i++) {
+ item = models[_i];
+ _results.push(model);
+ }
+ return _results;
+ }).call(this));
+ }
+ return models;
+ };
+
+ Cursor.prototype.prepareIncludes = function(json, callback) {
+ var findOrNew, include, item, model_json, related_json, relation, schema, shared_related_models, _i, _j, _len, _len1, _ref,
+ _this = this;
+ if (!_.isArray(this._cursor.$include) || _.isEmpty(this._cursor.$include)) {
+ return callback(null, json);
+ }
+ schema = this.model_type.schema();
+ shared_related_models = {};
+ findOrNew = function(related_json, reverse_model_type) {
+ var related_id;
+ related_id = related_json[reverse_model_type.prototype.idAttribute];
+ if (!shared_related_models[related_id]) {
+ if (reverse_model_type.cache) {
+ if (!(shared_related_models[related_id] = reverse_model_type.cache.get(related_id))) {
+ reverse_model_type.cache.set(related_id, shared_related_models[related_id] = new reverse_model_type(related_json));
+ }
+ } else {
+ shared_related_models[related_id] = new reverse_model_type(related_json);
+ }
+ }
+ return shared_related_models[related_id];
+ };
+ _ref = this._cursor.$include;
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ include = _ref[_i];
+ relation = schema.relation(include);
+ shared_related_models = {};
+ for (_j = 0, _len1 = json.length; _j < _len1; _j++) {
+ model_json = json[_j];
+ if (_.isArray(related_json = model_json[include])) {
+ model_json[include] = (function() {
+ var _k, _len2, _results;
+ _results = [];
+ for (_k = 0, _len2 = related_json.length; _k < _len2; _k++) {
+ item = related_json[_k];
+ _results.push(findOrNew(item, relation.reverse_model_type));
+ }
+ return _results;
+ })();
+ } else if (related_json) {
+ model_json[include] = findOrNew(related_json, relation.reverse_model_type);
+ }
+ }
+ }
+ return callback(null, json);
+ };
+
+ return Cursor;
+
+}).call(this);
+
+});
+
+;require.register("backbone-orm/lib/database_url", function(exports, require, module) {
+/*
+ backbone-orm.js 0.5.3
+ Copyright (c) 2013 Vidigami - https://github.com/vidigami/backbone-orm
+ License: MIT (http://www.opensource.org/licenses/mit-license.php)
+ Dependencies: Backbone.js, Underscore.js, Moment.js, and Inflection.js.
+*/
+
+var DatabaseURL, SUPPORTED_KEYS, URL, inflection, _;
+
+_ = require('underscore');
+
+inflection = require('inflection');
+
+URL = require('url');
+
+SUPPORTED_KEYS = ['protocol', 'slashes', 'auth', 'host', 'hostname', 'port', 'search', 'query', 'hash', 'href'];
+
+module.exports = DatabaseURL = (function() {
+ function DatabaseURL(url, parse_query_string, slashes_denote_host) {
+ var database, database_parts, databases, databases_string, host, key, parts, path_paths, start_parts, start_url, url_parts, _i, _j, _k, _len, _len1, _len2, _ref;
+ url_parts = URL.parse(url, parse_query_string, slashes_denote_host);
+ parts = url_parts.pathname.split(',');
+ if (parts.length > 1) {
+ start_parts = _.pick(url_parts, 'protocol', 'auth', 'slashes');
+ start_parts.host = '{1}';
+ start_parts.pathname = '{2}';
+ start_url = URL.format(start_parts);
+ start_url = start_url.replace('{1}/{2}', '');
+ path_paths = url_parts.pathname.split('/');
+ url_parts.pathname = "/" + path_paths[path_paths.length - 2] + "/" + path_paths[path_paths.length - 1];
+ databases_string = url.replace(start_url, '');
+ databases_string = databases_string.substring(0, databases_string.indexOf(url_parts.pathname));
+ databases = databases_string.split(',');
+ _ref = ['host', 'hostname', 'port'];
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ key = _ref[_i];
+ delete url_parts[key];
+ }
+ this.hosts = [];
+ for (_j = 0, _len1 = databases.length; _j < _len1; _j++) {
+ database = databases[_j];
+ host = database.split(':');
+ this.hosts.push(host.length === 1 ? {
+ host: host[0],
+ hostname: host[0]
+ } : {
+ host: host[0],
+ hostname: "" + host[0] + ":" + host[1],
+ port: host[1]
+ });
+ }
+ }
+ database_parts = url_parts.pathname.split('/');
+ this.table = database_parts.pop();
+ this.database = database_parts[database_parts.length - 1];
+ for (_k = 0, _len2 = SUPPORTED_KEYS.length; _k < _len2; _k++) {
+ key = SUPPORTED_KEYS[_k];
+ if (url_parts.hasOwnProperty(key)) {
+ this[key] = url_parts[key];
+ }
+ }
+ }
+
+ DatabaseURL.prototype.format = function(options) {
+ var host_strings, url, url_parts;
+ if (options == null) {
+ options = {};
+ }
+ url_parts = _.pick(this, SUPPORTED_KEYS);
+ url_parts.pathname = '';
+ if (this.hosts) {
+ host_strings = _.map(this.hosts, function(host) {
+ return "" + host.host + (host.port ? ':' + host.port : '');
+ });
+ url_parts.pathname += host_strings.join(',');
+ url_parts.host = "{1}";
+ }
+ if (this.database) {
+ url_parts.pathname += "/" + this.database;
+ }
+ if (this.table && !options.exclude_table) {
+ url_parts.pathname += "/" + this.table;
+ }
+ if (options.exclude_search || options.exclude_query) {
+ delete url_parts.search;
+ delete url_parts.query;
+ }
+ url = URL.format(url_parts);
+ if (this.hosts) {
+ url = url.replace("{1}/" + url_parts.pathname, url_parts.pathname);
+ }
+ return url;
+ };
+
+ DatabaseURL.prototype.parseAuth = function() {
+ var auth_parts, result;
+ if (!this.auth) {
+ return null;
+ }
+ auth_parts = this.auth.split(':');
+ result = {
+ user: auth_parts[0]
+ };
+ result.password = auth_parts.length > 1 ? auth_parts[1] : null;
+ return result;
+ };
+
+ DatabaseURL.prototype.modelName = function() {
+ if (this.table) {
+ return inflection.classify(inflection.singularize(this.table));
+ } else {
+ return null;
+ }
+ };
+
+ return DatabaseURL;
+
+})();
+
+});
+
+;require.register("backbone-orm/lib/extensions/collection", function(exports, require, module) {
+/*
+ backbone-orm.js 0.5.3
+ Copyright (c) 2013 Vidigami - https://github.com/vidigami/backbone-orm
+ License: MIT (http://www.opensource.org/licenses/mit-license.php)
+ Dependencies: Backbone.js, Underscore.js, Moment.js, and Inflection.js.
+*/
+
+var Backbone, Utils, collection_type, fn, key, overrides, _;
+
+_ = require('underscore');
+
+Backbone = require('backbone');
+
+Utils = require('../utils');
+
+collection_type = Backbone.Collection;
+
+overrides = {
+ fetch: function(options) {
+ var callback,
+ _this = this;
+ if (_.isFunction(callback = arguments[arguments.length - 1])) {
+ switch (arguments.length) {
+ case 1:
+ options = Utils.wrapOptions({}, callback);
+ break;
+ case 2:
+ options = Utils.wrapOptions(options, callback);
+ }
+ }
+ return collection_type.prototype._orm_original_fns.fetch.call(this, Utils.wrapOptions(options, function(err, model, resp, options) {
+ if (err) {
+ return typeof options.error === "function" ? options.error(_this, resp, options) : void 0;
+ }
+ return typeof options.success === "function" ? options.success(model, resp, options) : void 0;
+ }));
+ },
+ _prepareModel: function(attrs, options) {
+ var id, is_new, model;
+ if (!Utils.isModel(attrs) && (id = Utils.dataId(attrs))) {
+ if (this.model.cache) {
+ is_new = !!this.model.cache.get(id);
+ }
+ model = Utils.updateOrNew(attrs, this.model);
+ if (is_new && !model._validate(attrs, options)) {
+ this.trigger('invalid', this, attrs, options);
+ return false;
+ }
+ return model;
+ }
+ return collection_type.prototype._orm_original_fns._prepareModel.call(this, attrs, options);
+ }
+};
+
+if (!collection_type.prototype._orm_original_fns) {
+ collection_type.prototype._orm_original_fns = {};
+ for (key in overrides) {
+ fn = overrides[key];
+ collection_type.prototype._orm_original_fns[key] = collection_type.prototype[key];
+ collection_type.prototype[key] = fn;
+ }
+}
+
+});
+
+;require.register("backbone-orm/lib/extensions/model", function(exports, require, module) {
+/*
+ backbone-orm.js 0.5.3
+ Copyright (c) 2013 Vidigami - https://github.com/vidigami/backbone-orm
+ License: MIT (http://www.opensource.org/licenses/mit-license.php)
+ Dependencies: Backbone.js, Underscore.js, Moment.js, and Inflection.js.
+*/
+
+var Backbone, ModelStream, Queue, Utils, modelEach, modelInterval, moment, _;
+
+_ = require('underscore');
+
+Backbone = require('backbone');
+
+moment = require('moment');
+
+Queue = require('../queue');
+
+Utils = require('../utils');
+
+ModelStream = require('./model_stream');
+
+modelEach = require('./model_each');
+
+modelInterval = require('./model_interval');
+
+require('./collection');
+
+module.exports = function(model_type) {
+ var BackboneModelExtensions, fn, key, overrides, _findOrClone, _results;
+ BackboneModelExtensions = (function() {
+ function BackboneModelExtensions() {}
+
+ return BackboneModelExtensions;
+
+ })();
+ model_type.createSync = function(target_model_type) {
+ return model_type.prototype.sync('createSync', target_model_type);
+ };
+ model_type.resetSchema = function(options, callback) {
+ var _ref;
+ if (arguments.length === 1) {
+ _ref = [{}, options], options = _ref[0], callback = _ref[1];
+ }
+ return model_type.prototype.sync('resetSchema', options, callback);
+ };
+ model_type.cursor = function(query) {
+ if (query == null) {
+ query = {};
+ }
+ return model_type.prototype.sync('cursor', query);
+ };
+ model_type.destroy = function(query, callback) {
+ var _ref;
+ if (arguments.length === 1) {
+ _ref = [{}, query], query = _ref[0], callback = _ref[1];
+ }
+ if (!_.isObject(query)) {
+ query = {
+ id: query
+ };
+ }
+ return model_type.prototype.sync('destroy', query, callback);
+ };
+ model_type.db = function() {
+ return model_type.prototype.sync('db');
+ };
+ model_type.exists = function(query, callback) {
+ var _ref;
+ if (arguments.length === 1) {
+ _ref = [{}, query], query = _ref[0], callback = _ref[1];
+ }
+ return model_type.prototype.sync('cursor', query).exists(callback);
+ };
+ model_type.count = function(query, callback) {
+ var _ref;
+ if (arguments.length === 1) {
+ _ref = [{}, query], query = _ref[0], callback = _ref[1];
+ }
+ return model_type.prototype.sync('cursor', query).count(callback);
+ };
+ model_type.all = function(callback) {
+ return model_type.prototype.sync('cursor', {}).toModels(callback);
+ };
+ model_type.find = function(query, callback) {
+ var _ref;
+ if (arguments.length === 1) {
+ _ref = [{}, query], query = _ref[0], callback = _ref[1];
+ }
+ return model_type.prototype.sync('cursor', query).toModels(callback);
+ };
+ model_type.findOne = function(query, callback) {
+ var _ref;
+ if (arguments.length === 1) {
+ _ref = [{}, query], query = _ref[0], callback = _ref[1];
+ }
+ query = _.isObject(query) ? _.extend({
+ $one: true
+ }, query) : {
+ id: query,
+ $one: true
+ };
+ return model_type.prototype.sync('cursor', query).toModels(callback);
+ };
+ model_type.findOrCreate = function(data, callback) {
+ var query;
+ if (!_.isObject(data) || Utils.isModel(data) || Utils.isCollection(data)) {
+ throw 'findOrCreate requires object data';
+ }
+ query = _.extend({
+ $one: true
+ }, data);
+ return model_type.prototype.sync('cursor', query).toModels(function(err, model) {
+ if (err) {
+ return callback(err);
+ }
+ if (model) {
+ return callback(null, model);
+ }
+ return (new model_type(data)).save(callback);
+ });
+ };
+ model_type.findOneNearestDate = function(date, options, query, callback) {
+ var functions, key, _ref, _ref1,
+ _this = this;
+ if (!(key = options.key)) {
+ throw new Error("Missing options key");
+ }
+ if (arguments.length === 2) {
+ _ref = [{}, query], query = _ref[0], callback = _ref[1];
+ } else if (arguments.length === 3) {
+ _ref1 = [moment.utc().toDate(), {}, query], options = _ref1[0], query = _ref1[1], callback = _ref1[2];
+ } else {
+ query = _.clone(query);
+ }
+ query.$one = true;
+ functions = [
+ (function(callback) {
+ query[key] = {
+ $lte: date
+ };
+ return model_type.cursor(query).sort("-" + key).toModels(callback);
+ }), (function(callback) {
+ query[key] = {
+ $gte: date
+ };
+ return model_type.cursor(query).sort(key).toModels(callback);
+ })
+ ];
+ if (options.reverse) {
+ functions = [functions[1], functions[0]];
+ }
+ return functions[0](function(err, model) {
+ if (err) {
+ return callback(err);
+ }
+ if (model) {
+ return callback(null, model);
+ }
+ return functions[1](callback);
+ });
+ };
+ model_type.each = function(query, iterator, callback) {
+ var _ref;
+ if (arguments.length === 2) {
+ _ref = [{}, query, iterator], query = _ref[0], iterator = _ref[1], callback = _ref[2];
+ }
+ return modelEach(model_type, query, iterator, callback);
+ };
+ model_type.eachC = function(query, callback, iterator) {
+ var _ref;
+ if (arguments.length === 2) {
+ _ref = [{}, query, callback], query = _ref[0], callback = _ref[1], iterator = _ref[2];
+ }
+ return modelEach(model_type, query, iterator, callback);
+ };
+ model_type.stream = function(query) {
+ if (query == null) {
+ query = {};
+ }
+ if (!ModelStream) {
+ throw new Error('Stream is a large dependency so you need to manually include "stream.js" in the browser.');
+ }
+ return new ModelStream(model_type, query);
+ };
+ model_type.interval = function(query, iterator, callback) {
+ return modelInterval(model_type, query, iterator, callback);
+ };
+ model_type.intervalC = function(query, callback, iterator) {
+ return modelInterval(model_type, query, iterator, callback);
+ };
+ model_type.prototype.modelName = function() {
+ return model_type.model_name;
+ };
+ model_type.prototype.cache = function() {
+ return model_type.cache;
+ };
+ model_type.prototype.schema = model_type.schema = function() {
+ return model_type.prototype.sync('schema');
+ };
+ model_type.prototype.tableName = model_type.tableName = function() {
+ return model_type.prototype.sync('tableName');
+ };
+ model_type.prototype.relation = model_type.relation = function(key) {
+ var schema;
+ if (schema = model_type.prototype.sync('schema')) {
+ return schema.relation(key);
+ } else {
+ return void 0;
+ }
+ };
+ model_type.prototype.relationIsEmbedded = model_type.relationIsEmbedded = function(key) {
+ var relation;
+ if (relation = model_type.relation(key)) {
+ return !!relation.embed;
+ } else {
+ return false;
+ }
+ };
+ model_type.prototype.reverseRelation = model_type.reverseRelation = function(key) {
+ var schema;
+ if (schema = model_type.prototype.sync('schema')) {
+ return schema.reverseRelation(key);
+ } else {
+ return void 0;
+ }
+ };
+ model_type.prototype.isLoaded = function(key) {
+ if (arguments.length === 0) {
+ key = '__model__';
+ }
+ return !Utils.orSet(this, 'needs_load', {})[key];
+ };
+ model_type.prototype.setLoaded = function(key, is_loaded) {
+ var needs_load, _ref;
+ if (arguments.length === 1) {
+ _ref = ['__model__', key], key = _ref[0], is_loaded = _ref[1];
+ }
+ needs_load = Utils.orSet(this, 'needs_load', {});
+ if (is_loaded && Utils.get(this, 'is_initialized')) {
+ delete needs_load[key];
+ return;
+ }
+ return needs_load[key] = !is_loaded;
+ };
+ model_type.prototype.isLoadedExists = function(key) {
+ if (arguments.length === 0) {
+ key = '__model__';
+ }
+ return Utils.orSet(this, 'needs_load', {}).hasOwnProperty(key);
+ };
+ model_type.prototype.isPartial = function() {
+ return !!Utils.get(this, 'partial');
+ };
+ model_type.prototype.setPartial = function(is_partial) {
+ if (is_partial) {
+ return Utils.set(this, 'partial', true);
+ } else {
+ return Utils.unset(this, 'partial');
+ }
+ };
+ model_type.prototype.fetchRelated = function(relations, callback) {
+ var queue, _ref,
+ _this = this;
+ if (arguments.length === 1) {
+ _ref = [null, relations], relations = _ref[0], callback = _ref[1];
+ }
+ queue = new Queue(1);
+ queue.defer(function(callback) {
+ if (_this.isLoaded()) {
+ return callback();
+ }
+ return _this.fetch(callback);
+ });
+ queue.defer(function(callback) {
+ var key, keys, relations_queue, _fn, _i, _len;
+ keys = _.keys(Utils.orSet(_this, 'needs_load', {}));
+ if (relations && !_.isArray(relations)) {
+ relations = [relations];
+ }
+ if (_.isArray(relations)) {
+ keys = _.intersection(keys, relations);
+ }
+ relations_queue = new Queue();
+ _fn = function(key) {
+ return relations_queue.defer(function(callback) {
+ return _this.get(key, callback);
+ });
+ };
+ for (_i = 0, _len = keys.length; _i < _len; _i++) {
+ key = keys[_i];
+ _fn(key);
+ }
+ return relations_queue.await(callback);
+ });
+ return queue.await(callback);
+ };
+ model_type.prototype.patchAdd = function(key, relateds, callback) {
+ var relation;
+ if (!(relation = this.relation(key))) {
+ return callback(new Error("patchAdd: relation '" + key + "' unrecognized"));
+ }
+ if (!relateds) {
+ return callback(new Error("patchAdd: missing relateds for '" + key + "'"));
+ }
+ return relation.patchAdd(this, relateds, callback);
+ };
+ model_type.prototype.patchRemove = function(key, relateds, callback) {
+ var queue, relation, schema, _fn, _ref,
+ _this = this;
+ if (arguments.length === 1) {
+ callback = key;
+ schema = model_type.schema();
+ queue = new Queue(1);
+ _ref = schema.relations;
+ _fn = function(relation) {
+ return queue.defer(function(callback) {
+ return relation.patchRemove(_this, callback);
+ });
+ };
+ for (key in _ref) {
+ relation = _ref[key];
+ _fn(relation);
+ }
+ return queue.await(callback);
+ } else {
+ if (!(relation = this.relation(key))) {
+ return callback(new Error("patchRemove: relation '" + key + "' unrecognized"));
+ }
+ if (arguments.length === 2) {
+ callback = relateds;
+ return relation.patchRemove(this, callback);
+ } else {
+ if (!relateds) {
+ return callback(new Error("patchRemove: missing relateds for '" + key + "'"));
+ }
+ return relation.patchRemove(this, relateds, callback);
+ }
+ }
+ };
+ model_type.prototype.cursor = function(key, query) {
+ var relation, schema;
+ if (query == null) {
+ query = {};
+ }
+ if (model_type.schema) {
+ schema = model_type.schema();
+ }
+ if (schema && (relation = schema.relation(key))) {
+ return relation.cursor(this, key, query);
+ } else {
+ throw new Error("" + schema.model_name + "::cursor: Unexpected key: " + key + " is not a relation");
+ }
+ };
+ _findOrClone = function(model, options) {
+ var cache, clone, _base, _name;
+ if (model.isNew() || !model.modelName) {
+ return model.clone(options);
+ }
+ cache = (_base = options._cache)[_name = model.modelName()] || (_base[_name] = {});
+ if (!(clone = cache[model.id])) {
+ clone = cache[model.id] = model.clone(options);
+ }
+ return clone;
+ };
+ overrides = {
+ initialize: function(attributes) {
+ var key, needs_load, relation, schema, value, _ref;
+ if (model_type.schema && (schema = model_type.schema())) {
+ _ref = schema.relations;
+ for (key in _ref) {
+ relation = _ref[key];
+ relation.initializeModel(this);
+ }
+ needs_load = Utils.orSet(this, 'needs_load', {});
+ for (key in needs_load) {
+ value = needs_load[key];
+ if (!value) {
+ delete needs_load[key];
+ }
+ }
+ Utils.set(this, 'is_initialized', true);
+ }
+ return model_type.prototype._orm_original_fns.initialize.apply(this, arguments);
+ },
+ fetch: function(options) {
+ var callback,
+ _this = this;
+ if (_.isFunction(callback = arguments[arguments.length - 1])) {
+ switch (arguments.length) {
+ case 1:
+ options = Utils.wrapOptions({}, callback);
+ break;
+ case 2:
+ options = Utils.wrapOptions(options, callback);
+ }
+ } else {
+ options || (options = {});
+ }
+ return model_type.prototype._orm_original_fns.fetch.call(this, Utils.wrapOptions(options, function(err, model, resp, options) {
+ if (err) {
+ return typeof options.error === "function" ? options.error(_this, resp, options) : void 0;
+ }
+ _this.setLoaded(true);
+ return typeof options.success === "function" ? options.success(_this, resp, options) : void 0;
+ }));
+ },
+ unset: function(key) {
+ var id;
+ id = this.id;
+ model_type.prototype._orm_original_fns.unset.apply(this, arguments);
+ if (key === 'id' && model_type.cache && id && (model_type.cache.get(id) === this)) {
+ return model_type.cache.destroy(id);
+ }
+ },
+ set: function(key, value, options) {
+ var attributes, relation, relational_attributes, schema, simple_attributes;
+ if (!(model_type.schema && (schema = model_type.schema()))) {
+ return model_type.prototype._orm_original_fns.set.apply(this, arguments);
+ }
+ if (_.isString(key)) {
+ (attributes = {})[key] = value;
+ } else {
+ attributes = key;
+ options = value;
+ }
+ simple_attributes = {};
+ relational_attributes = {};
+ for (key in attributes) {
+ value = attributes[key];
+ if (relation = schema.relation(key)) {
+ relational_attributes[key] = relation;
+ } else {
+ simple_attributes[key] = value;
+ }
+ }
+ if (_.size(simple_attributes)) {
+ model_type.prototype._orm_original_fns.set.call(this, simple_attributes, options);
+ }
+ for (key in relational_attributes) {
+ relation = relational_attributes[key];
+ relation.set(this, key, attributes[key], options);
+ }
+ return this;
+ },
+ get: function(key, callback) {
+ var relation, schema, value;
+ if (model_type.schema) {
+ schema = model_type.schema();
+ }
+ if (schema && (relation = schema.relation(key))) {
+ return relation.get(this, key, callback);
+ }
+ value = model_type.prototype._orm_original_fns.get.call(this, key);
+ if (callback) {
+ callback(null, value);
+ }
+ return value;
+ },
+ toJSON: function(options) {
+ var json, key, keys, relation, schema, value, _base, _i, _len;
+ if (options == null) {
+ options = {};
+ }
+ if (model_type.schema) {
+ schema = model_type.schema();
+ }
+ this._orm || (this._orm = {});
+ if (this._orm.json > 0) {
+ return this.id;
+ }
+ (_base = this._orm).json || (_base.json = 0);
+ this._orm.json++;
+ json = {};
+ keys = options.keys || this.whitelist || _.keys(this.attributes);
+ for (_i = 0, _len = keys.length; _i < _len; _i++) {
+ key = keys[_i];
+ value = this.attributes[key];
+ if (schema && (relation = schema.relation(key))) {
+ relation.appendJSON(json, this);
+ } else if (Utils.isCollection(value)) {
+ json[key] = _.map(value.models, function(model) {
+ if (model) {
+ return model.toJSON(options);
+ } else {
+ return null;
+ }
+ });
+ } else if (Utils.isModel(value)) {
+ json[key] = value.toJSON(options);
+ } else {
+ json[key] = value;
+ }
+ }
+ --this._orm.json;
+ return json;
+ },
+ save: function(key, value, options) {
+ var attributes, callback, _base,
+ _this = this;
+ if (_.isFunction(callback = arguments[arguments.length - 1])) {
+ switch (arguments.length) {
+ case 1:
+ attributes = {};
+ options = Utils.wrapOptions({}, callback);
+ break;
+ case 2:
+ attributes = key;
+ options = Utils.wrapOptions({}, callback);
+ break;
+ case 3:
+ attributes = key;
+ options = Utils.wrapOptions(value, callback);
+ break;
+ case 4:
+ (attributes = {})[key] = value;
+ options = Utils.wrapOptions(options, callback);
+ }
+ } else {
+ if (arguments.length === 0) {
+ attributes = {};
+ options = {};
+ } else if (key === null || _.isObject(key)) {
+ attributes = key;
+ options = value;
+ } else {
+ (attributes = {})[key] = value;
+ }
+ }
+ if (!this.isLoaded()) {
+ return typeof options.error === "function" ? options.error(this, new Error("An unloaded model is trying to be saved: " + model_type.model_name)) : void 0;
+ }
+ this._orm || (this._orm = {});
+ if (this._orm.save > 0) {
+ if (this.id) {
+ return typeof options.success === "function" ? options.success(this, {}, options) : void 0;
+ }
+ return typeof options.error === "function" ? options.error(this, new Error("Model is in a save loop: " + model_type.model_name)) : void 0;
+ }
+ (_base = this._orm).save || (_base.save = 0);
+ this._orm.save++;
+ this.set(attributes, options);
+ attributes = {};
+ return Utils.presaveBelongsToRelationships(this, function(err) {
+ if (err) {
+ return typeof options.error === "function" ? options.error(_this, err) : void 0;
+ }
+ return model_type.prototype._orm_original_fns.save.call(_this, attributes, Utils.wrapOptions(options, function(err, model, resp, options) {
+ var queue, relation, schema, _fn, _ref;
+ --_this._orm.save;
+ if (err) {
+ return typeof options.error === "function" ? options.error(_this, resp, options) : void 0;
+ }
+ queue = new Queue(1);
+ if (model_type.schema) {
+ schema = model_type.schema();
+ _ref = schema.relations;
+ _fn = function(relation) {
+ return queue.defer(function(callback) {
+ return relation.save(_this, callback);
+ });
+ };
+ for (key in _ref) {
+ relation = _ref[key];
+ _fn(relation);
+ }
+ }
+ return queue.await(function(err) {
+ var cache;
+ if (err) {
+ return typeof options.error === "function" ? options.error(_this, Error("Failed to save relations. " + err, options)) : void 0;
+ }
+ if (cache = model_type.cache) {
+ cache.set(_this.id, _this);
+ }
+ return typeof options.success === "function" ? options.success(_this, resp, options) : void 0;
+ });
+ }));
+ });
+ },
+ destroy: function(options) {
+ var cache, callback, schema, _base,
+ _this = this;
+ if (_.isFunction(callback = arguments[arguments.length - 1])) {
+ switch (arguments.length) {
+ case 1:
+ options = Utils.wrapOptions({}, callback);
+ break;
+ case 2:
+ options = Utils.wrapOptions(options, callback);
+ }
+ }
+ if (cache = this.cache()) {
+ cache.destroy(this.id);
+ }
+ if (!(model_type.schema && (schema = model_type.schema()))) {
+ return model_type.prototype._orm_original_fns.destroy.call(this, options);
+ }
+ this._orm || (this._orm = {});
+ if (this._orm.destroy > 0) {
+ throw new Error("Model is in a destroy loop: " + model_type.model_name);
+ }
+ (_base = this._orm).destroy || (_base.destroy = 0);
+ this._orm.destroy++;
+ return model_type.prototype._orm_original_fns.destroy.call(this, Utils.wrapOptions(options, function(err, model, resp, options) {
+ --_this._orm.destroy;
+ if (err) {
+ return typeof options.error === "function" ? options.error(_this, resp, options) : void 0;
+ }
+ return _this.patchRemove(function(err) {
+ if (err) {
+ return typeof options.error === "function" ? options.error(_this, new Error("Failed to destroy relations. " + err, options)) : void 0;
+ }
+ return typeof options.success === "function" ? options.success(_this, resp, options) : void 0;
+ });
+ }));
+ },
+ clone: function(options) {
+ var cache, clone, key, keys, model, value, _base, _base1, _i, _len, _name, _ref;
+ if (!model_type.schema) {
+ return model_type.prototype._orm_original_fns.clone.apply(this, arguments);
+ }
+ options || (options = {});
+ options._cache || (options._cache = {});
+ cache = (_base = options._cache)[_name = this.modelName()] || (_base[_name] = {});
+ this._orm || (this._orm = {});
+ if (this._orm.clone > 0) {
+ if (this.id) {
+ return cache[this.id];
+ } else {
+ return model_type.prototype._orm_original_fns.clone.apply(this, arguments);
+ }
+ }
+ (_base1 = this._orm).clone || (_base1.clone = 0);
+ this._orm.clone++;
+ if (this.id) {
+ if (!(clone = cache[this.id])) {
+ cache[this.id] = clone = new this.constructor();
+ }
+ } else {
+ clone = new this.constructor();
+ }
+ if (this.attributes.id) {
+ clone.id = this.attributes.id;
+ }
+ keys = options.keys || _.keys(this.attributes);
+ for (_i = 0, _len = keys.length; _i < _len; _i++) {
+ key = keys[_i];
+ value = this.attributes[key];
+ if (Utils.isCollection(value)) {
+ if (!((_ref = clone.attributes[key]) != null ? _ref.values : void 0)) {
+ clone.attributes[key] = new value.constructor();
+ }
+ clone.attributes[key].models = (function() {
+ var _j, _len1, _ref1, _results;
+ _ref1 = value.models;
+ _results = [];
+ for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
+ model = _ref1[_j];
+ _results.push(_findOrClone(model, options));
+ }
+ return _results;
+ })();
+ } else if (Utils.isModel(value)) {
+ clone.attributes[key] = _findOrClone(value, options);
+ } else {
+ clone.attributes[key] = value;
+ }
+ }
+ --this._orm.clone;
+ return clone;
+ }
+ };
+ if (!model_type.prototype._orm_original_fns) {
+ model_type.prototype._orm_original_fns = {};
+ _results = [];
+ for (key in overrides) {
+ fn = overrides[key];
+ model_type.prototype._orm_original_fns[key] = model_type.prototype[key];
+ _results.push(model_type.prototype[key] = fn);
+ }
+ return _results;
+ }
+};
+
+});
+
+;require.register("backbone-orm/lib/extensions/model_each", function(exports, require, module) {
+var BATCH_DEFAULT_FETCH, Cursor, Queue, _;
+
+_ = require('underscore');
+
+Queue = require('../queue');
+
+Cursor = null;
+
+BATCH_DEFAULT_FETCH = 1000;
+
+module.exports = function(model_type, query, iterator, callback) {
+ var method, model_limit, options, parsed_query, processed_count, runBatch;
+ if (!Cursor) {
+ Cursor = require('../cursor');
+ }
+ options = query.$each || {};
+ method = options.json ? 'toJSON' : 'toModels';
+ processed_count = 0;
+ parsed_query = Cursor.parseQuery(_.omit(query, '$each'));
+ _.defaults(parsed_query.cursor, {
+ $offset: 0,
+ $sort: 'id'
+ });
+ model_limit = parsed_query.cursor.$limit || Infinity;
+ parsed_query.cursor.$limit = options.fetch || BATCH_DEFAULT_FETCH;
+ runBatch = function() {
+ var cursor;
+ cursor = model_type.cursor(parsed_query);
+ return cursor[method].call(cursor, function(err, models) {
+ var model, queue, _fn, _i, _len;
+ if (err || !models) {
+ return callback(new Error("Failed to get models. Error: " + err));
+ }
+ if (!models.length) {
+ return callback(null, processed_count);
+ }
+ queue = new Queue(options.threads);
+ _fn = function(model) {
+ return queue.defer(function(callback) {
+ return iterator(model, callback);
+ });
+ };
+ for (_i = 0, _len = models.length; _i < _len; _i++) {
+ model = models[_i];
+ if (processed_count++ >= model_limit) {
+ break;
+ }
+ _fn(model);
+ }
+ return queue.await(function(err) {
+ if (err) {
+ return callback(err);
+ }
+ if (processed_count >= model_limit) {
+ return callback(null, processed_count);
+ }
+ if (models.length < parsed_query.cursor.$limit) {
+ return callback(null, processed_count);
+ }
+ parsed_query.cursor.$offset += parsed_query.cursor.$limit;
+ return runBatch();
+ });
+ });
+ };
+ return runBatch();
+};
+
+});
+
+;require.register("backbone-orm/lib/extensions/model_interval", function(exports, require, module) {
+var INTERVAL_TYPES, Queue, moment, _;
+
+_ = require('underscore');
+
+moment = require('moment');
+
+Queue = require('../queue');
+
+INTERVAL_TYPES = ['milliseconds', 'seconds', 'minutes', 'hours', 'days', 'weeks', 'months', 'years'];
+
+module.exports = function(model_type, query, iterator, callback) {
+ var iteration_info, key, no_models, options, queue, range;
+ options = query.$interval || {};
+ if (!(key = options.key)) {
+ throw new Error('missing option: key');
+ }
+ if (!options.type) {
+ throw new Error('missing option: type');
+ }
+ if (!_.contains(INTERVAL_TYPES, options.type)) {
+ throw new Error("type is not recognized: " + options.type + ", " + (_.contains(INTERVAL_TYPES, options.type)));
+ }
+ iteration_info = _.clone(options);
+ if (!iteration_info.range) {
+ iteration_info.range = {};
+ }
+ range = iteration_info.range;
+ no_models = false;
+ queue = new Queue(1);
+ queue.defer(function(callback) {
+ var start;
+ if (!(start = range.$gte || range.$gt)) {
+ return model_type.cursor(query).limit(1).sort(key).toModels(function(err, models) {
+ if (err) {
+ return callback(err);
+ }
+ if (!models.length) {
+ no_models = true;
+ return callback();
+ }
+ range.start = iteration_info.first = models[0].get(key);
+ return callback();
+ });
+ } else {
+ range.start = start;
+ return model_type.findOneNearestDate(start, {
+ key: key,
+ reverse: true
+ }, query, function(err, model) {
+ if (err) {
+ return callback(err);
+ }
+ if (!model) {
+ no_models = true;
+ return callback();
+ }
+ iteration_info.first = model.get(key);
+ return callback();
+ });
+ }
+ });
+ queue.defer(function(callback) {
+ var end;
+ if (no_models) {
+ return callback();
+ }
+ if (!(end = range.$lte || range.$lt)) {
+ return model_type.cursor(query).limit(1).sort("-" + key).toModels(function(err, models) {
+ if (err) {
+ return callback(err);
+ }
+ if (!models.length) {
+ no_models = true;
+ return callback();
+ }
+ range.end = iteration_info.last = models[0].get(key);
+ return callback();
+ });
+ } else {
+ range.end = end;
+ return model_type.findOneNearestDate(end, {
+ key: key
+ }, query, function(err, model) {
+ if (err) {
+ return callback(err);
+ }
+ if (!model) {
+ no_models = true;
+ return callback();
+ }
+ iteration_info.last = model.get(key);
+ return callback();
+ });
+ }
+ });
+ return queue.await(function(err) {
+ var length_ms, processed_count, runInterval, start_ms;
+ if (err) {
+ return callback(err);
+ }
+ if (no_models) {
+ return callback();
+ }
+ start_ms = range.start.getTime();
+ length_ms = moment.duration((_.isUndefined(options.length) ? 1 : options.length), options.type).asMilliseconds();
+ if (!length_ms) {
+ throw Error("length_ms is invalid: " + length_ms + " for range: " + (util.inspect(range)));
+ }
+ query = _.omit(query, '$interval');
+ query.$sort = [key];
+ processed_count = 0;
+ iteration_info.index = 0;
+ runInterval = function(current) {
+ if (current.isAfter(range.end)) {
+ return callback();
+ }
+ query[key] = {
+ $gte: current.toDate(),
+ $lte: iteration_info.last
+ };
+ return model_type.findOne(query, function(err, model) {
+ var next;
+ if (err) {
+ return callback(err);
+ }
+ if (!model) {
+ return callback();
+ }
+ next = model.get(key);
+ iteration_info.index = Math.floor((next.getTime() - start_ms) / length_ms);
+ current = moment.utc(range.start).add({
+ milliseconds: iteration_info.index * length_ms
+ });
+ iteration_info.start = current.toDate();
+ next = current.clone().add({
+ milliseconds: length_ms
+ });
+ iteration_info.end = next.toDate();
+ query[key] = {
+ $gte: current.toDate(),
+ $lt: next.toDate()
+ };
+ return iterator(query, iteration_info, function(err) {
+ if (err) {
+ return callback(err);
+ }
+ return runInterval(next);
+ });
+ });
+ };
+ return runInterval(moment(range.start));
+ });
+};
+
+});
+
+;require.register("backbone-orm/lib/extensions/model_stream", function(exports, require, module) {
+var ModelStream, e, stream,
+ __hasProp = {}.hasOwnProperty,
+ __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
+
+try {
+ stream = require('stream');
+} catch (_error) {
+ e = _error;
+}
+
+if (!(stream != null ? stream.Readable : void 0)) {
+ module.exports = null;
+ return;
+}
+
+module.exports = ModelStream = (function(_super) {
+ __extends(ModelStream, _super);
+
+ function ModelStream(model_type, query) {
+ this.model_type = model_type;
+ this.query = query != null ? query : {};
+ ModelStream.__super__.constructor.call(this, {
+ objectMode: true
+ });
+ }
+
+ ModelStream.prototype._read = function() {
+ var done,
+ _this = this;
+ if (this.ended || this.started) {
+ return;
+ }
+ this.started = true;
+ done = function(err) {
+ _this.ended = true;
+ if (err) {
+ _this.emit('error', err);
+ }
+ return _this.push(null);
+ };
+ return this.model_type.each(this.query, (function(model, callback) {
+ _this.push(model);
+ return callback();
+ }), done);
+ };
+
+ return ModelStream;
+
+})(stream.Readable);
+
+});
+
+;require.register("backbone-orm/lib/index", function(exports, require, module) {
+/*
+ backbone-orm.js 0.5.3
+ Copyright (c) 2013 Vidigami - https://github.com/vidigami/backbone-orm
+ License: MIT (http://www.opensource.org/licenses/mit-license.php)
+ Dependencies: Backbone.js, Underscore.js, Moment.js, and Inflection.js.
+*/
+
+var e;
+
+if ((typeof window !== "undefined" && window !== null) && require.shim) {
+ require.shim([
+ {
+ symbol: '_',
+ path: 'lodash',
+ alias: 'underscore',
+ optional: true
+ }, {
+ symbol: '_',
+ path: 'underscore'
+ }, {
+ symbol: 'Backbone',
+ path: 'backbone'
+ }, {
+ symbol: 'moment',
+ path: 'moment'
+ }, {
+ symbol: 'inflection',
+ path: 'inflection'
+ }, {
+ symbol: 'stream',
+ path: 'stream',
+ optional: true
+ }
+ ]);
+}
+
+module.exports = {
+ sync: require('./memory/sync'),
+ Utils: require('./utils'),
+ JSONUtils: require('./json_utils'),
+ Queue: require('./queue'),
+ DatabaseURL: require('./database_url'),
+ modules: {
+ url: require('url'),
+ querystring: require('querystring'),
+ 'lru-cache': require('lru-cache'),
+ underscore: require('underscore'),
+ backbone: require('backbone'),
+ moment: require('moment'),
+ inflection: require('inflection')
+ },
+ Cursor: require('./cursor'),
+ Schema: require('./schema'),
+ ConnectionPool: require('./connection_pool'),
+ CacheSingletons: require('./cache/singletons')
+};
+
+try {
+ module.exports.modules.stream = require('stream');
+} catch (_error) {
+ e = _error;
+}
+
+});
+
+;require.register("backbone-orm/lib/json_utils", function(exports, require, module) {
+/*
+ backbone-orm.js 0.5.3
+ Copyright (c) 2013 Vidigami - https://github.com/vidigami/backbone-orm
+ License: MIT (http://www.opensource.org/licenses/mit-license.php)
+ Dependencies: Backbone.js, Underscore.js, Moment.js, and Inflection.js.
+*/
+
+var JSONUtils, Queue, moment, _;
+
+_ = require('underscore');
+
+moment = require('moment');
+
+Queue = require('./queue');
+
+module.exports = JSONUtils = (function() {
+ var _this = this;
+
+ function JSONUtils() {}
+
+ JSONUtils.parseParams = function(params) {
+ var key, result, value;
+ result = {};
+ for (key in params) {
+ value = params[key];
+ result[key] = JSON.parse(value);
+ }
+ return result;
+ };
+
+ JSONUtils.parse = function(values) {
+ var date, err, key, match, result, value;
+ if (_.isNull(values) || (values === 'null')) {
+ return null;
+ }
+ if (_.isDate(values)) {
+ return values;
+ }
+ if (_.isArray(values)) {
+ return _.map(values, JSONUtils.parse);
+ }
+ if (_.isObject(values)) {
+ result = {};
+ for (key in values) {
+ value = values[key];
+ result[key] = JSONUtils.parse(value);
+ }
+ return result;
+ } else if (_.isString(values)) {
+ if ((values.length >= 20) && values[values.length - 1] === 'Z') {
+ date = moment.utc(values);
+ if (date && date.isValid()) {
+ return date.toDate();
+ } else {
+ return values;
+ }
+ }
+ if (values === 'true') {
+ return true;
+ }
+ if (values === 'false') {
+ return false;
+ }
+ if (match = /^\"(.*)\"$/.exec(values)) {
+ return match[0];
+ }
+ try {
+ if (values = JSON.parse(values)) {
+ return JSONUtils.parse(values);
+ }
+ } catch (_error) {
+ err = _error;
+ }
+ }
+ return values;
+ };
+
+ JSONUtils.toQuery = function(values, depth) {
+ var key, result, value;
+ if (depth == null) {
+ depth = 0;
+ }
+ if (_.isNull(values)) {
+ return 'null';
+ }
+ if (_.isArray(values)) {
+ return JSON.stringify(values);
+ }
+ if (_.isDate(values) || values.toJSON) {
+ return values.toJSON();
+ }
+ if (_.isObject(values)) {
+ if (depth > 0) {
+ return JSON.stringify(values);
+ }
+ result = {};
+ for (key in values) {
+ value = values[key];
+ result[key] = JSONUtils.toQuery(value, 1);
+ }
+ return result;
+ }
+ return values;
+ };
+
+ JSONUtils.renderTemplate = function(models, template, options, callback) {
+ var model, queue, results, _fn, _i, _len;
+ if (arguments.length === 3) {
+ callback = options;
+ options = {};
+ }
+ if (!_.isArray(models)) {
+ if (!models) {
+ return callback(null, null);
+ }
+ if (_.isString(template)) {
+ return JSONUtils.renderKey(models, template, options, callback);
+ }
+ if (_.isArray(template)) {
+ return JSONUtils.renderKeys(models, template, options, callback);
+ }
+ if (_.isFunction(template)) {
+ return template(models, options, callback);
+ }
+ return JSONUtils.renderDSL(models, template, options, callback);
+ } else {
+ results = [];
+ queue = new Queue(1);
+ _fn = function(model) {
+ return queue.defer(function(callback) {
+ return JSONUtils.renderTemplate(model, template, options, function(err, related_json) {
+ if (err) {
+ return callback(err);
+ }
+ results.push(related_json);
+ return callback();
+ });
+ });
+ };
+ for (_i = 0, _len = models.length; _i < _len; _i++) {
+ model = models[_i];
+ _fn(model);
+ }
+ return queue.await(function(err) {
+ return callback(err, err ? void 0 : results);
+ });
+ }
+ };
+
+ JSONUtils.renderDSL = function(model, dsl, options, callback) {
+ var args, key, queue, result, _fn;
+ if (arguments.length === 3) {
+ callback = options;
+ options = {};
+ }
+ queue = new Queue();
+ result = {};
+ _fn = function(key, args) {
+ return queue.defer(function(callback) {
+ var field, fn_args, query, relation, template;
+ field = args.key || key;
+ if (relation = model.relation(field)) {
+ if (args.query) {
+ query = args.query;
+ template = args.template;
+ } else if (args.$count) {
+ query = _.clone(args);
+ delete query.key;
+ } else if (_.isFunction(args)) {
+ template = args;
+ } else if (args.template) {
+ if (_.isObject(args.template) && !_.isFunction(args.template)) {
+ query = args.template;
+ } else {
+ template = args.template;
+ query = _.clone(args);
+ delete query.key;
+ delete query.template;
+ if (_.size(query) === 0) {
+ query = null;
+ }
+ }
+ } else {
+ template = _.clone(args);
+ delete template.key;
+ }
+ if (template) {
+ if (query) {
+ return relation.cursor(model, field, query).toModels(function(err, models) {
+ if (err) {
+ return callback(err);
+ }
+ return JSONUtils.renderTemplate(models, template, options, function(err, json) {
+ result[key] = json;
+ return callback(err);
+ });
+ });
+ } else {
+ return model.get(field, function(err, related_model) {
+ if (err) {
+ return callback(err);
+ }
+ return JSONUtils.renderTemplate(related_model, template, options, function(err, json) {
+ result[key] = json;
+ return callback(err);
+ });
+ });
+ }
+ } else {
+ return relation.cursor(model, field, query).toJSON(function(err, json) {
+ result[key] = json;
+ return callback(err);
+ });
+ }
+ } else {
+ if (key.length > 1 && key[key.length - 1] === '_') {
+ key = key.slice(0, +(key.length - 2) + 1 || 9e9);
+ }
+ if (key === '$select') {
+ if (_.isString(args)) {
+ return JSONUtils.renderKey(model, args, options, function(err, json) {
+ result[args] = json;
+ return callback(err);
+ });
+ } else {
+ return JSONUtils.renderKeys(model, args, options, function(err, json) {
+ _.extend(result, json);
+ return callback(err);
+ });
+ }
+ } else if (_.isString(args)) {
+ return JSONUtils.renderKey(model, args, options, function(err, json) {
+ result[key] = json;
+ return callback(err);
+ });
+ } else if (_.isFunction(args)) {
+ return args(model, options, function(err, json) {
+ result[key] = json;
+ return callback(err);
+ });
+ } else if (_.isString(args.method)) {
+ fn_args = _.isArray(args.args) ? args.args.slice() : (args.args ? [args.args] : []);
+ fn_args.push(function(err, json) {
+ result[key] = json;
+ return callback();
+ });
+ return model[args.method].apply(model, fn_args);
+ } else {
+ console.trace("Unknown DSL action: " + key + ": ", args);
+ return callback(new Error("Unknown DSL action: " + key + ": ", args));
+ }
+ }
+ });
+ };
+ for (key in dsl) {
+ args = dsl[key];
+ _fn(key, args);
+ }
+ return queue.await(function(err) {
+ return callback(err, err ? void 0 : result);
+ });
+ };
+
+ JSONUtils.renderKeys = function(model, keys, options, callback) {
+ var key, queue, result, _fn, _i, _len;
+ if (arguments.length === 3) {
+ callback = options;
+ options = {};
+ }
+ result = {};
+ queue = new Queue();
+ _fn = function(key) {
+ return queue.defer(function(callback) {
+ return JSONUtils.renderKey(model, key, options, function(err, value) {
+ if (err) {
+ return callback(err);
+ }
+ result[key] = value;
+ return callback();
+ });
+ });
+ };
+ for (_i = 0, _len = keys.length; _i < _len; _i++) {
+ key = keys[_i];
+ _fn(key);
+ }
+ return queue.await(function(err) {
+ return callback(err, err ? void 0 : result);
+ });
+ };
+
+ JSONUtils.renderKey = function(model, key, options, callback) {
+ if (arguments.length === 3) {
+ callback = options;
+ options = {};
+ }
+ return model.get(key, function(err, value) {
+ var item;
+ if (err) {
+ return callback(err);
+ }
+ if (model.relation(key)) {
+ if (_.isArray(value)) {
+ return callback(null, (function() {
+ var _i, _len, _results;
+ _results = [];
+ for (_i = 0, _len = value.length; _i < _len; _i++) {
+ item = value[_i];
+ _results.push(item.toJSON());
+ }
+ return _results;
+ })());
+ }
+ if (value && value.toJSON) {
+ return callback(null, value = value.toJSON());
+ }
+ }
+ return callback(null, value);
+ });
+ };
+
+ JSONUtils.renderRelated = function(models, attribute_name, template, options, callback) {
+ var model, queue, results, _fn, _i, _len;
+ if (arguments.length === 4) {
+ callback = options;
+ options = {};
+ }
+ if (!_.isArray(models)) {
+ return models.get(attribute_name, function(err, related_models) {
+ if (err) {
+ callback(err);
+ }
+ return JSONUtils.renderTemplate(related_models, template, options, callback);
+ });
+ } else {
+ results = [];
+ queue = new Queue();
+ _fn = function(model) {
+ return queue.defer(function(callback) {
+ return model.get(attribute_name, function(err, related_models) {
+ if (err) {
+ callback(err);
+ }
+ return JSONUtils.renderTemplate(related_models, template, options, function(err, related_json) {
+ if (err) {
+ return callback(err);
+ }
+ results.push(related_json);
+ return callback();
+ });
+ });
+ });
+ };
+ for (_i = 0, _len = models.length; _i < _len; _i++) {
+ model = models[_i];
+ _fn(model);
+ }
+ return queue.await(function(err) {
+ return callback(err, err ? void 0 : results);
+ });
+ }
+ };
+
+ JSONUtils.deepClone = function(obj, depth) {
+ var clone, key;
+ if (!obj || (typeof obj !== 'object')) {
+ return obj;
+ }
+ if (_.isString(obj)) {
+ return String.prototype.slice.call(obj);
+ }
+ if (_.isDate(obj)) {
+ return new Date(obj.valueOf());
+ }
+ if (_.isFunction(obj.clone)) {
+ return obj.clone();
+ }
+ if (_.isArray(obj)) {
+ clone = Array.prototype.slice.call(obj);
+ } else if (obj.constructor !== {}.constructor) {
+ return obj;
+ } else {
+ clone = _.extend({}, obj);
+ }
+ if (!_.isUndefined(depth) && (depth > 0)) {
+ for (key in clone) {
+ clone[key] = JSONUtils.deepClone(clone[key], depth - 1);
+ }
+ }
+ return clone;
+ };
+
+ return JSONUtils;
+
+}).call(this);
+
+});
+
+;require.register("backbone-orm/lib/memory/cursor", function(exports, require, module) {
+/*
+ backbone-orm.js 0.5.3
+ Copyright (c) 2013 Vidigami - https://github.com/vidigami/backbone-orm
+ License: MIT (http://www.opensource.org/licenses/mit-license.php)
+ Dependencies: Backbone.js, Underscore.js, Moment.js, and Inflection.js.
+*/
+
+var Cursor, IS_MATCH_FNS, IS_MATCH_OPERATORS, JSONUtils, MemoryCursor, Queue, Utils, inflection, moment, _, _ref,
+ __hasProp = {}.hasOwnProperty,
+ __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
+ __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
+
+_ = require('underscore');
+
+moment = require('moment');
+
+inflection = require('inflection');
+
+Queue = require('../queue');
+
+Utils = require('../utils');
+
+JSONUtils = require('../json_utils');
+
+Cursor = require('../cursor');
+
+IS_MATCH_FNS = {
+ $ne: function(mv, tv) {
+ return !_.isEqual(mv, tv);
+ },
+ $lt: function(mv, tv) {
+ if (_.isNull(tv)) {
+ throw Error('Cannot compare to null');
+ }
+ return (_.isDate(tv) ? moment(mv).isBefore(tv) : mv < tv);
+ },
+ $lte: function(mv, tv) {
+ var mvm;
+ if (_.isNull(tv)) {
+ throw Error('Cannot compare to null');
+ }
+ if (_.isDate(tv)) {
+ mvm = moment(mv);
+ return mvm.isBefore(tv) || mvm.isSame(tv);
+ } else {
+ return (mv < tv) || _.isEqual(mv, tv);
+ }
+ },
+ $gt: function(mv, tv) {
+ if (_.isNull(tv)) {
+ throw Error('Cannot compare to null');
+ }
+ return (_.isDate(tv) ? moment(mv).isAfter(tv) : mv > tv);
+ },
+ $gte: function(mv, tv) {
+ var mvm;
+ if (_.isNull(tv)) {
+ throw Error('Cannot compare to null');
+ }
+ if (_.isDate(tv)) {
+ mvm = moment(mv);
+ return mvm.isAfter(tv) || mvm.isSame(tv);
+ } else {
+ return (mv > tv) || _.isEqual(mv, tv);
+ }
+ }
+};
+
+IS_MATCH_OPERATORS = _.keys(IS_MATCH_FNS);
+
+module.exports = MemoryCursor = (function(_super) {
+ __extends(MemoryCursor, _super);
+
+ function MemoryCursor() {
+ _ref = MemoryCursor.__super__.constructor.apply(this, arguments);
+ return _ref;
+ }
+
+ MemoryCursor.prototype.queryToJSON = function(callback) {
+ var exists,
+ _this = this;
+ if (this.hasCursorQuery('$zero')) {
+ return callback(null, this.hasCursorQuery('$one') ? null : []);
+ }
+ exists = this.hasCursorQuery('$exists');
+ return this.buildFindQuery(function(err, find_query) {
+ var json, keys, queue;
+ if (err) {
+ return callback(err);
+ }
+ json = [];
+ keys = _.keys(find_query);
+ queue = new Queue(1);
+ queue.defer(function(callback) {
+ var find_queue, id, ins, ins_size, key, model_json, value, _fn, _ref1, _ref2, _ref3;
+ ins = {};
+ for (key in find_query) {
+ value = find_query[key];
+ if (value != null ? value.$in : void 0) {
+ delete find_query[key];
+ ins[key] = value.$in;
+ }
+ }
+ ins_size = _.size(ins);
+ if (keys.length || ins_size) {
+ if (_this._cursor.$ids) {
+ _ref1 = _this.store;
+ for (id in _ref1) {
+ model_json = _ref1[id];
+ if (_.contains(_this._cursor.$ids, id) && _.isEqual(_.pick(model_json, keys), find_query)) {
+ json.push(JSONUtils.deepClone(model_json));
+ }
+ }
+ return callback();
+ } else {
+ find_queue = new Queue();
+ _ref2 = _this.store;
+ _fn = function(model_json) {
+ return find_queue.defer(function(callback) {
+ var find_keys, next;
+ find_keys = _.keys(find_query);
+ next = function(err, is_match) {
+ if (err) {
+ return callback(err);
+ }
+ if (!is_match) {
+ return callback();
+ }
+ if (!find_keys.length || (exists && (keys.length !== find_keys.length))) {
+ json.push(JSONUtils.deepClone(model_json));
+ return callback();
+ }
+ return _this._valueIsMatch(find_query, find_keys.pop(), model_json, next);
+ };
+ return next(null, true);
+ });
+ };
+ for (id in _ref2) {
+ model_json = _ref2[id];
+ _fn(model_json);
+ }
+ return find_queue.await(function(err) {
+ if (err) {
+ return callback(err);
+ }
+ if (ins_size) {
+ json = _.filter(json, function(model_json) {
+ var values, _ref3;
+ for (key in ins) {
+ values = ins[key];
+ if (_ref3 = model_json[key], __indexOf.call(values, _ref3) >= 0) {
+ return true;
+ }
+ }
+ });
+ }
+ return callback();
+ });
+ }
+ } else {
+ if (_this._cursor.$ids) {
+ _ref3 = _this.store;
+ for (id in _ref3) {
+ model_json = _ref3[id];
+ if (_.contains(_this._cursor.$ids, id)) {
+ json.push(JSONUtils.deepClone(model_json));
+ }
+ }
+ } else {
+ json = (function() {
+ var _ref4, _results;
+ _ref4 = this.store;
+ _results = [];
+ for (id in _ref4) {
+ model_json = _ref4[id];
+ _results.push(JSONUtils.deepClone(model_json));
+ }
+ return _results;
+ }).call(_this);
+ }
+ return callback();
+ }
+ });
+ if (!exists) {
+ queue.defer(function(callback) {
+ var $sort_fields, number;
+ if (_this._cursor.$sort) {
+ $sort_fields = _.isArray(_this._cursor.$sort) ? _this._cursor.$sort : [_this._cursor.$sort];
+ json.sort(function(model, next_model) {
+ return Utils.jsonFieldCompare(model, next_model, $sort_fields);
+ });
+ }
+ if (_this._cursor.$offset) {
+ number = json.length - _this._cursor.$offset;
+ if (number < 0) {
+ number = 0;
+ }
+ json = number ? json.slice(_this._cursor.$offset, _this._cursor.$offset + number) : [];
+ }
+ if (_this._cursor.$one) {
+ json = json.length ? [json[0]] : [];
+ } else if (_this._cursor.$limit) {
+ json = json.splice(0, Math.min(json.length, _this._cursor.$limit));
+ }
+ return callback();
+ });
+ queue.defer(function(callback) {
+ return _this.fetchIncludes(json, callback);
+ });
+ }
+ queue.await(function() {
+ var count_cursor;
+ if (_this.hasCursorQuery('$count')) {
+ return callback(null, (_.isArray(json) ? json.length : (json ? 1 : 0)));
+ }
+ if (exists) {
+ return callback(null, (_.isArray(json) ? !!json.length : json));
+ }
+ json = _this.selectResults(json);
+ if (_this.hasCursorQuery('$page')) {
+ count_cursor = new MemoryCursor(_this._find, _.extend(_.pick(_this, ['model_type', 'store'])));
+ return count_cursor.count(function(err, count) {
+ return callback(null, {
+ offset: _this._cursor.$offset || 0,
+ total_rows: count,
+ rows: json
+ });
+ });
+ } else {
+ return callback(null, json);
+ }
+ });
+ });
+ };
+
+ MemoryCursor.prototype.buildFindQuery = function(callback) {
+ var find_query, key, queue, relation_key, reverse_relation, value, value_key, _fn, _ref1, _ref2,
+ _this = this;
+ queue = new Queue();
+ find_query = {};
+ _ref1 = this._find;
+ _fn = function(relation_key, value_key, value) {
+ return queue.defer(function(callback) {
+ var related_query, relation;
+ if (!(relation = _this.model_type.relation(relation_key))) {
+ find_query[key] = value;
+ return callback();
+ }
+ if (!relation.join_table && (value_key === 'id')) {
+ find_query[relation.foreign_key] = value;
+ return callback();
+ } else if (relation.join_table || (relation.type === 'belongsTo')) {
+ (related_query = {
+ $values: 'id'
+ })[value_key] = value;
+ return relation.reverse_relation.model_type.cursor(related_query).toJSON(function(err, related_ids) {
+ var join_query;
+ if (err) {
+ return callback(err);
+ }
+ if (relation.join_table) {
+ (join_query = {})[relation.reverse_relation.join_key] = {
+ $in: _.compact(related_ids)
+ };
+ join_query.$values = relation.foreign_key;
+ return relation.join_table.cursor(join_query).toJSON(function(err, model_ids) {
+ if (err) {
+ return callback(err);
+ }
+ find_query.id = {
+ $in: _.compact(model_ids)
+ };
+ return callback();
+ });
+ } else {
+ find_query[relation.foreign_key] = {
+ $in: _.compact(related_ids)
+ };
+ return callback();
+ }
+ });
+ } else {
+ (related_query = {})[value_key] = value;
+ related_query.$values = relation.foreign_key;
+ return relation.reverse_model_type.cursor(related_query).toJSON(function(err, model_ids) {
+ if (err) {
+ return callback(err);
+ }
+ find_query.id = {
+ $in: _.compact(model_ids)
+ };
+ return callback();
+ });
+ }
+ });
+ };
+ for (key in _ref1) {
+ value = _ref1[key];
+ if (key.indexOf('.') < 0) {
+ if (!(reverse_relation = this.model_type.reverseRelation(key))) {
+ find_query[key] = value;
+ continue;
+ }
+ if (!reverse_relation.embed && !reverse_relation.join_table) {
+ find_query[key] = value;
+ continue;
+ }
+ (function(key, value, reverse_relation) {
+ return queue.defer(function(callback) {
+ var related_query;
+ if (reverse_relation.embed) {
+ throw Error("Embedded find is not yet supported. @_find: " + (util.inspect(_this._find)));
+ (related_query = {}).id = value;
+ return reverse_relation.model_type.cursor(related_query).toJSON(function(err, models_json) {
+ if (err) {
+ return callback(err);
+ }
+ find_query._json = _.map(models_json, function(test) {
+ return test[reverse_relation.key];
+ });
+ return callback();
+ });
+ } else {
+ (related_query = {})[key] = value;
+ related_query.$values = reverse_relation.reverse_relation.join_key;
+ return reverse_relation.join_table.cursor(related_query).toJSON(function(err, model_ids) {
+ if (err) {
+ return callback(err);
+ }
+ find_query.id = {
+ $in: model_ids
+ };
+ return callback();
+ });
+ }
+ });
+ })(key, value, reverse_relation);
+ continue;
+ }
+ _ref2 = key.split('.'), relation_key = _ref2[0], value_key = _ref2[1];
+ if (this.model_type.relationIsEmbedded(relation_key)) {
+ find_query[key] = value;
+ continue;
+ }
+ _fn(relation_key, value_key, value);
+ }
+ return queue.await(function(err) {
+ return callback(err, find_query);
+ });
+ };
+
+ MemoryCursor.prototype.fetchIncludes = function(json, callback) {
+ var include_keys, key, load_queue, model_json, relation, _fn, _i, _j, _len, _len1,
+ _this = this;
+ if (!this._cursor.$include) {
+ return callback();
+ }
+ load_queue = new Queue(1);
+ include_keys = _.isArray(this._cursor.$include) ? this._cursor.$include : [this._cursor.$include];
+ for (_i = 0, _len = include_keys.length; _i < _len; _i++) {
+ key = include_keys[_i];
+ if (this.model_type.relationIsEmbedded(key)) {
+ continue;
+ }
+ if (!(relation = this.model_type.relation(key))) {
+ return callback(new Error("Included relation '" + key + "' is not a relation"));
+ }
+ _fn = function(key, model_json) {
+ return load_queue.defer(function(callback) {
+ return relation.cursor(model_json, key).toJSON(function(err, related_json) {
+ if (err) {
+ return calback(err);
+ }
+ delete model_json[relation.foriegn_key];
+ model_json[key] = related_json;
+ return callback();
+ });
+ });
+ };
+ for (_j = 0, _len1 = json.length; _j < _len1; _j++) {
+ model_json = json[_j];
+ _fn(key, model_json);
+ }
+ }
+ return load_queue.await(callback);
+ };
+
+ MemoryCursor.prototype._valueIsMatch = function(find_query, key_path, model_json, callback) {
+ var key_components, model_type, next,
+ _this = this;
+ key_components = key_path.split('.');
+ model_type = this.model_type;
+ next = function(err, models_json) {
+ var find_value, is_match, key, model_value, operator, relation, was_handled, _i, _j, _len, _len1;
+ if (err) {
+ return callback(err);
+ }
+ key = key_components.shift();
+ if (key === 'id') {
+ key = model_type.prototype.idAttribute;
+ }
+ if (!key_components.length) {
+ was_handled = false;
+ find_value = find_query[key_path];
+ if (!_.isArray(models_json)) {
+ models_json = [models_json];
+ }
+ for (_i = 0, _len = models_json.length; _i < _len; _i++) {
+ model_json = models_json[_i];
+ model_value = model_json[key];
+ if (_.isObject(find_value)) {
+ for (_j = 0, _len1 = IS_MATCH_OPERATORS.length; _j < _len1; _j++) {
+ operator = IS_MATCH_OPERATORS[_j];
+ if (!(find_value.hasOwnProperty(operator))) {
+ continue;
+ }
+ was_handled = true;
+ if (!(is_match = IS_MATCH_FNS[operator](model_value, find_value[operator]))) {
+ break;
+ }
+ }
+ }
+ if (was_handled) {
+ if (is_match) {
+ return callback(null, is_match);
+ }
+ } else if (is_match = _.isEqual(model_value, find_value)) {
+ return callback(null, is_match);
+ }
+ }
+ return callback(null, false);
+ }
+ if ((relation = model_type.relation(key)) && !relation.embed) {
+ return relation.cursor(model_json, key).toJSON(next);
+ }
+ return next(null, model_json[key]);
+ };
+ return next(null, model_json);
+ };
+
+ return MemoryCursor;
+
+})(Cursor);
+
+});
+
+;require.register("backbone-orm/lib/memory/sync", function(exports, require, module) {
+/*
+ backbone-orm.js 0.5.3
+ Copyright (c) 2013 Vidigami - https://github.com/vidigami/backbone-orm
+ License: MIT (http://www.opensource.org/licenses/mit-license.php)
+ Dependencies: Backbone.js, Underscore.js, Moment.js, and Inflection.js.
+*/
+
+var Backbone, DESTROY_BATCH_LIMIT, JSONUtils, MemoryCursor, MemorySync, ModelCache, QueryCache, Queue, STORES, Schema, Utils, _;
+
+_ = require('underscore');
+
+Backbone = require('backbone');
+
+Queue = require('../queue');
+
+MemoryCursor = require('./cursor');
+
+Schema = require('../schema');
+
+Utils = require('../utils');
+
+JSONUtils = require('../json_utils');
+
+ModelCache = require('../cache/singletons').ModelCache;
+
+QueryCache = require('../cache/singletons').QueryCache;
+
+DESTROY_BATCH_LIMIT = 1000;
+
+STORES = {};
+
+MemorySync = (function() {
+ function MemorySync(model_type) {
+ var _name;
+ this.model_type = model_type;
+ this.model_type.model_name = Utils.findOrGenerateModelName(this.model_type);
+ this.schema = new Schema(this.model_type);
+ this.store = this.model_type.store = STORES[_name = this.model_type.model_name] || (STORES[_name] = {});
+ }
+
+ MemorySync.prototype.initialize = function() {
+ if (this.is_initialized) {
+ return;
+ }
+ this.is_initialized = true;
+ return this.schema.initialize();
+ };
+
+ MemorySync.prototype.read = function(model, options) {
+ var id, model_json;
+ if (model.models) {
+ return options.success((function() {
+ var _ref, _results;
+ _ref = this.store;
+ _results = [];
+ for (id in _ref) {
+ model_json = _ref[id];
+ _results.push(JSONUtils.deepClone(model_json));
+ }
+ return _results;
+ }).call(this));
+ } else {
+ if (_.isUndefined(this.store[model.id])) {
+ return options.error(new Error("Model not found with id: " + model.id));
+ }
+ return options.success(JSONUtils.deepClone(this.store[model.id]));
+ }
+ };
+
+ MemorySync.prototype.create = function(model, options) {
+ var _this = this;
+ return QueryCache.reset(this.model_type, function(err) {
+ var attributes, model_json;
+ if (err) {
+ return typeof options.error === "function" ? options.error(err) : void 0;
+ }
+ (attributes = {})[_this.model_type.prototype.idAttribute] = Utils.guid();
+ model.set(attributes);
+ model_json = _this.store[model.id] = model.toJSON();
+ return options.success(JSONUtils.deepClone(model_json));
+ });
+ };
+
+ MemorySync.prototype.update = function(model, options) {
+ var _this = this;
+ return QueryCache.reset(this.model_type, function(err) {
+ var model_json;
+ if (err) {
+ return typeof options.error === "function" ? options.error(err) : void 0;
+ }
+ _this.store[model.id] = model_json = model.toJSON();
+ return options.success(JSONUtils.deepClone(model_json));
+ });
+ };
+
+ MemorySync.prototype["delete"] = function(model, options) {
+ var _this = this;
+ return QueryCache.reset(this.model_type, function(err) {
+ if (err) {
+ return typeof options.error === "function" ? options.error(err) : void 0;
+ }
+ if (!_this.store[model.id]) {
+ return options.error(new Error('Model not found'));
+ }
+ delete _this.store[model.id];
+ return options.success();
+ });
+ };
+
+ MemorySync.prototype.resetSchema = function(options, callback) {
+ var _this = this;
+ return QueryCache.reset(this.model_type, function(err) {
+ if (err) {
+ return callback(err);
+ }
+ return _this.destroy({}, callback);
+ });
+ };
+
+ MemorySync.prototype.cursor = function(query) {
+ if (query == null) {
+ query = {};
+ }
+ return new MemoryCursor(query, _.pick(this, ['model_type', 'store']));
+ };
+
+ MemorySync.prototype.destroy = function(query, callback) {
+ var _this = this;
+ return QueryCache.reset(this.model_type, function(err) {
+ if (err) {
+ return callback(err);
+ }
+ return _this.model_type.each(_.extend({
+ $each: {
+ limit: DESTROY_BATCH_LIMIT,
+ json: true
+ }
+ }, query), (function(model_json, callback) {
+ return Utils.patchRemoveByJSON(_this.model_type, model_json, function(err) {
+ if (!err) {
+ delete _this.store[model_json[_this.model_type.prototype.idAttribute]];
+ }
+ return callback(err);
+ });
+ }), callback);
+ });
+ };
+
+ return MemorySync;
+
+})();
+
+module.exports = function(type) {
+ var model_type, sync, sync_fn;
+ if (Utils.isCollection(new type())) {
+ model_type = Utils.configureCollectionModelType(type, module.exports);
+ return type.prototype.sync = model_type.prototype.sync;
+ }
+ sync = new MemorySync(type);
+ type.prototype.sync = sync_fn = function(method, model, options) {
+ if (options == null) {
+ options = {};
+ }
+ sync.initialize();
+ if (method === 'createSync') {
+ return module.exports.apply(null, Array.prototype.slice.call(arguments, 1));
+ }
+ if (method === 'sync') {
+ return sync;
+ }
+ if (method === 'isRemote') {
+ return false;
+ }
+ if (method === 'schema') {
+ return sync.schema;
+ }
+ if (method === 'tableName') {
+ return void 0;
+ }
+ if (sync[method]) {
+ return sync[method].apply(sync, Array.prototype.slice.call(arguments, 1));
+ } else {
+ return void 0;
+ }
+ };
+ Utils.configureModelType(type);
+ return ModelCache.configureSync(type, sync_fn);
+};
+
+});
+
+;require.register("backbone-orm/lib/queue", function(exports, require, module) {
+/*
+ backbone-orm.js 0.5.3
+ Copyright (c) 2013 Vidigami - https://github.com/vidigami/backbone-orm
+ License: MIT (http://www.opensource.org/licenses/mit-license.php)
+ Dependencies: Backbone.js, Underscore.js, Moment.js, and Inflection.js.
+*/
+
+var Queue,
+ __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
+
+module.exports = Queue = (function() {
+ function Queue(parallelism) {
+ this.parallelism = parallelism;
+ this._doneTask = __bind(this._doneTask, this);
+ this.parallelism || (this.parallelism = Infinity);
+ this.tasks = [];
+ this.running_count = 0;
+ this.error = null;
+ this.await_callback = null;
+ }
+
+ Queue.prototype.defer = function(callback) {
+ this.tasks.push(callback);
+ return this._runTasks();
+ };
+
+ Queue.prototype.await = function(callback) {
+ if (this.await_callback) {
+ throw new Error("Awaiting callback was added twice: " + callback);
+ }
+ this.await_callback = callback;
+ if (this.error || !(this.tasks.length + this.running_count)) {
+ return this._callAwaiting();
+ }
+ };
+
+ Queue.prototype._doneTask = function(err) {
+ this.running_count--;
+ this.error || (this.error = err);
+ return this._runTasks();
+ };
+
+ Queue.prototype._runTasks = function() {
+ var current;
+ if (this.error || !(this.tasks.length + this.running_count)) {
+ return this._callAwaiting();
+ }
+ while (this.running_count < this.parallelism) {
+ if (!this.tasks.length) {
+ return;
+ }
+ current = this.tasks.shift();
+ this.running_count++;
+ current(this._doneTask);
+ }
+ };
+
+ Queue.prototype._callAwaiting = function() {
+ if (this.await_called || !this.await_callback) {
+ return;
+ }
+ this.await_called = true;
+ return this.await_callback(this.error);
+ };
+
+ return Queue;
+
+})();
+
+});
+
+;require.register("backbone-orm/lib/relations/many", function(exports, require, module) {
+/*
+ backbone-orm.js 0.5.3
+ Copyright (c) 2013 Vidigami - https://github.com/vidigami/backbone-orm
+ License: MIT (http://www.opensource.org/licenses/mit-license.php)
+ Dependencies: Backbone.js, Underscore.js, Moment.js, and Inflection.js.
+*/
+
+var Backbone, Many, Queue, Utils, inflection, _,
+ __hasProp = {}.hasOwnProperty,
+ __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
+
+Backbone = require('backbone');
+
+_ = require('underscore');
+
+inflection = require('inflection');
+
+Queue = require('../queue');
+
+Utils = require('../utils');
+
+module.exports = Many = (function(_super) {
+ __extends(Many, _super);
+
+ function Many(model_type, key, options) {
+ var Collection, value, _ref;
+ this.model_type = model_type;
+ this.key = key;
+ for (key in options) {
+ value = options[key];
+ this[key] = value;
+ }
+ this.virtual_id_accessor || (this.virtual_id_accessor = "" + (inflection.singularize(this.key)) + "_ids");
+ if (!this.join_key) {
+ this.join_key = this.foreign_key || inflection.foreign_key(this.model_type.model_name);
+ }
+ if (!this.foreign_key) {
+ this.foreign_key = inflection.foreign_key(this.as || this.model_type.model_name);
+ }
+ if (!this.collection_type) {
+ Collection = (function(_super1) {
+ __extends(Collection, _super1);
+
+ function Collection() {
+ _ref = Collection.__super__.constructor.apply(this, arguments);
+ return _ref;
+ }
+
+ Collection.prototype.model = Collection.reverse_model_type;
+
+ return Collection;
+
+ })(Backbone.Collection);
+ this.collection_type = Collection;
+ }
+ }
+
+ Many.prototype.initialize = function() {
+ var _ref;
+ this.reverse_relation = this._findOrGenerateReverseRelation(this);
+ if (this.embed && this.reverse_relation && this.reverse_relation.embed) {
+ throw new Error("Both relationship directions cannot embed (" + this.model_type.model_name + " and " + this.reverse_model_type.model_name + "). Choose one or the other.");
+ }
+ if (((_ref = this.reverse_relation) != null ? _ref.type : void 0) === 'hasOne') {
+ throw new Error("The reverse of a hasMany relation should be `belongsTo`, not `hasOne` (" + this.model_type.model_name + " and " + this.reverse_model_type.model_name + ").");
+ }
+ if (this.reverse_relation.type === 'hasMany') {
+ return this.join_table = this.findOrGenerateJoinTable(this);
+ }
+ };
+
+ Many.prototype.initializeModel = function(model) {
+ if (!model.isLoadedExists(this.key)) {
+ model.setLoaded(this.key, false);
+ }
+ return this._bindBacklinks(model);
+ };
+
+ Many.prototype.releaseModel = function(model) {
+ this._unbindBacklinks(model);
+ return delete model._orm;
+ };
+
+ Many.prototype.set = function(model, key, value, options) {
+ var collection, item, model_ids, models, previous_models, related_model, _i, _len;
+ if (!((key === this.key) || (key === this.virtual_id_accessor) || (key === this.foreign_key))) {
+ throw new Error("Many.set: Unexpected key " + key + ". Expecting: " + this.key + " or " + this.virtual_id_accessor + " or " + this.foreign_key);
+ }
+ collection = this._bindBacklinks(model);
+ if (Utils.isCollection(value)) {
+ value = value.models;
+ }
+ if (_.isUndefined(value)) {
+ value = [];
+ }
+ if (!_.isArray(value)) {
+ throw new Error("HasMany.set: Unexpected type to set " + key + ". Expecting array: " + (util.inspect(value)));
+ }
+ Utils.orSet(model, 'rel_dirty', {})[this.key] = true;
+ model.setLoaded(this.key, _.all(value, function(item) {
+ return Utils.dataId(item) !== item;
+ }));
+ models = (function() {
+ var _i, _len, _results;
+ _results = [];
+ for (_i = 0, _len = value.length; _i < _len; _i++) {
+ item = value[_i];
+ _results.push((related_model = collection.get(Utils.dataId(item))) ? Utils.updateModel(related_model, item) : Utils.updateOrNew(item, this.reverse_model_type));
+ }
+ return _results;
+ }).call(this);
+ model.setLoaded(this.key, _.all(models, function(model) {
+ return model.isLoaded();
+ }));
+ previous_models = _.clone(collection.models);
+ collection.reset(models);
+ if (this.reverse_relation.type === 'belongsTo') {
+ model_ids = _.pluck(models, 'id');
+ for (_i = 0, _len = previous_models.length; _i < _len; _i++) {
+ related_model = previous_models[_i];
+ if (!_.contains(model_ids, related_model.id)) {
+ related_model.set(this.foreign_key, null);
+ }
+ }
+ }
+ return this;
+ };
+
+ Many.prototype.get = function(model, key, callback) {
+ var collection, is_loaded, result, returnValue,
+ _this = this;
+ if (!((key === this.key) || (key === this.virtual_id_accessor) || (key === this.foreign_key))) {
+ throw new Error("Many.get: Unexpected key " + key + ". Expecting: " + this.key + " or " + this.virtual_id_accessor + " or " + this.foreign_key);
+ }
+ collection = this._ensureCollection(model);
+ returnValue = function() {
+ var related_model, _i, _len, _ref, _results;
+ if (key === _this.virtual_id_accessor) {
+ _ref = collection.models;
+ _results = [];
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ related_model = _ref[_i];
+ _results.push(related_model.id);
+ }
+ return _results;
+ } else {
+ return collection;
+ }
+ };
+ if (callback && !this.isVirtual() && !this.manual_fetch && !(is_loaded = model.isLoaded(this.key))) {
+ this.cursor(model, this.key).toJSON(function(err, json) {
+ var cache, model_json, related_model, result, _i, _j, _len, _len1, _ref;
+ if (err) {
+ return callback(err);
+ }
+ model.setLoaded(_this.key, true);
+ for (_i = 0, _len = json.length; _i < _len; _i++) {
+ model_json = json[_i];
+ if (related_model = collection.get(model_json[_this.reverse_model_type.prototype.idAttribute])) {
+ related_model.set(model_json);
+ } else {
+ collection.add(related_model = Utils.updateOrNew(model_json, _this.reverse_model_type));
+ }
+ }
+ if (cache = _this.reverse_model_type.cache) {
+ _ref = collection.models;
+ for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) {
+ related_model = _ref[_j];
+ cache.set(related_model.id, related_model);
+ }
+ }
+ result = returnValue();
+ return callback(null, result.models ? result.models : result);
+ });
+ }
+ result = returnValue();
+ if (callback && (is_loaded || this.manual_fetch)) {
+ callback(null, result.models ? result.models : result);
+ }
+ return result;
+ };
+
+ Many.prototype.save = function(model, callback) {
+ var collection;
+ if (!this._hasChanged(model)) {
+ return callback();
+ }
+ delete Utils.orSet(model, 'rel_dirty', {})[this.key];
+ collection = this._ensureCollection(model);
+ return this._saveRelated(model, _.clone(collection.models), callback);
+ };
+
+ Many.prototype.appendJSON = function(json, model) {
+ var collection, json_key;
+ if (this.isVirtual()) {
+ return;
+ }
+ collection = this._ensureCollection(model);
+ json_key = this.embed ? this.key : this.virtual_id_accessor;
+ if (this.embed) {
+ return json[json_key] = collection.toJSON();
+ }
+ };
+
+ Many.prototype.add = function(model, related_model) {
+ var collection, current_related_model;
+ collection = this._ensureCollection(model);
+ current_related_model = collection.get(related_model.id);
+ if (current_related_model === related_model) {
+ return;
+ }
+ if (current_related_model) {
+ collection.remove(current_related_model);
+ }
+ if (this.reverse_model_type.cache && related_model.id) {
+ this.reverse_model_type.cache.set(related_model.id, related_model);
+ }
+ return collection.add(related_model);
+ };
+
+ Many.prototype.remove = function(model, related_model) {
+ var collection, current_related_model;
+ collection = this._ensureCollection(model);
+ if (!(current_related_model = collection.get(related_model.id))) {
+ return;
+ }
+ return collection.remove(current_related_model);
+ };
+
+ Many.prototype.patchAdd = function(model, relateds, callback) {
+ var collection, item, query, queue, related, related_id, related_ids, related_model, _fn, _i, _j, _len, _len1,
+ _this = this;
+ if (!model.id) {
+ return callback(new Error("Many.patchAdd: model has null id for: " + this.key));
+ }
+ if (!relateds) {
+ return callback(new Error("Many.patchAdd: missing model for: " + this.key));
+ }
+ if (!_.isArray(relateds)) {
+ relateds = [relateds];
+ }
+ collection = this._ensureCollection(model);
+ relateds = (function() {
+ var _i, _len, _results;
+ _results = [];
+ for (_i = 0, _len = relateds.length; _i < _len; _i++) {
+ item = relateds[_i];
+ _results.push((related_model = collection.get(Utils.dataId(item))) ? Utils.updateModel(related_model, item) : Utils.updateOrNew(item, this.reverse_model_type));
+ }
+ return _results;
+ }).call(this);
+ related_ids = (function() {
+ var _i, _len, _results;
+ _results = [];
+ for (_i = 0, _len = relateds.length; _i < _len; _i++) {
+ related = relateds[_i];
+ _results.push(Utils.dataId(related));
+ }
+ return _results;
+ })();
+ collection.add(relateds);
+ if (model.isLoaded(this.key)) {
+ for (_i = 0, _len = relateds.length; _i < _len; _i++) {
+ related = relateds[_i];
+ if (!related.isLoaded()) {
+ model.setLoaded(this.key, false);
+ break;
+ }
+ }
+ }
+ if (this.join_table) {
+ queue = new Queue(1);
+ _fn = function(related_id) {
+ return queue.defer(function(callback) {
+ var add, query;
+ if (!related_id) {
+ return callback(new Error("Many.patchAdd: cannot add an new model. Please save first."));
+ }
+ add = function(callback) {
+ var attributes, join;
+ attributes = {};
+ attributes[_this.foreign_key] = model.id;
+ attributes[_this.reverse_relation.foreign_key] = related_id;
+ join = new _this.join_table(attributes);
+ return join.save(callback);
+ };
+ if (_this.reverse_relation.type === 'hasMany') {
+ return add(callback);
+ }
+ (query = {})[_this.reverse_relation.foreign_key] = related_id;
+ return _this.join_table.find(query, function(err, join_table_json) {
+ if (err) {
+ return callback(err);
+ }
+ if (!join_table_json) {
+ return add(callback);
+ }
+ if (join_table_json[_this.foreign_key] === model.id) {
+ return callback();
+ }
+ join_table_json[_this.foreign_key] = model.id;
+ return Utils.modelJSONSave(join_table_json, _this.join_table, callback);
+ });
+ });
+ };
+ for (_j = 0, _len1 = related_ids.length; _j < _len1; _j++) {
+ related_id = related_ids[_j];
+ _fn(related_id);
+ }
+ return queue.await(callback);
+ } else {
+ query = {
+ id: {
+ $in: related_ids
+ }
+ };
+ return this.reverse_model_type.cursor(query).toJSON(function(err, related_jsons) {
+ var related_json, _fn1, _k, _len2;
+ queue = new Queue(1);
+ _fn1 = function(related_json) {
+ return queue.defer(function(callback) {
+ related_json[_this.reverse_relation.foreign_key] = model.id;
+ return Utils.modelJSONSave(related_json, _this.reverse_model_type, callback);
+ });
+ };
+ for (_k = 0, _len2 = related_jsons.length; _k < _len2; _k++) {
+ related_json = related_jsons[_k];
+ _fn1(related_json);
+ }
+ return queue.await(callback);
+ });
+ }
+ };
+
+ Many.prototype.patchRemove = function(model, relateds, callback) {
+ var cache, collection, current_related_model, json, query, related, related_ids, related_model, related_models, _i, _j, _k, _len, _len1, _len2, _ref,
+ _this = this;
+ if (!model.id) {
+ return callback(new Error("Many.patchRemove: model has null id for: " + this.key));
+ }
+ if (arguments.length === 2) {
+ callback = relateds;
+ if (!this.reverse_relation) {
+ return callback();
+ }
+ if (Utils.isModel(model)) {
+ delete Utils.orSet(model, 'rel_dirty', {})[this.key];
+ collection = this._ensureCollection(model);
+ related_models = _.clone(collection.models);
+ } else {
+ related_models = (function() {
+ var _i, _len, _ref, _results;
+ _ref = model[this.key] || [];
+ _results = [];
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ json = _ref[_i];
+ _results.push(new this.reverse_model_type(json));
+ }
+ return _results;
+ }).call(this);
+ }
+ for (_i = 0, _len = related_models.length; _i < _len; _i++) {
+ related_model = related_models[_i];
+ related_model.set(this.foreign_key, null);
+ if (cache = related_model.cache()) {
+ cache.set(related_model.id, related_model);
+ }
+ }
+ if (this.join_table) {
+ (query = {})[this.join_key] = model.id;
+ return this.join_table.destroy(query, callback);
+ } else {
+ (query = {})[this.reverse_relation.foreign_key] = model.id;
+ this.reverse_model_type.cursor(query).toJSON(function(err, json) {
+ var queue, related_json, _fn, _j, _len1;
+ if (err) {
+ return callback(err);
+ }
+ queue = new Queue(1);
+ _fn = function(related_json) {
+ return queue.defer(function(callback) {
+ related_json[_this.reverse_relation.foreign_key] = null;
+ return Utils.modelJSONSave(related_json, _this.reverse_model_type, callback);
+ });
+ };
+ for (_j = 0, _len1 = json.length; _j < _len1; _j++) {
+ related_json = json[_j];
+ _fn(related_json);
+ }
+ return queue.await(callback);
+ });
+ }
+ return;
+ }
+ if (this.isEmbedded()) {
+ return callback(new Error('Many.patchRemove: embedded relationships are not supported'));
+ }
+ if (!relateds) {
+ return callback(new Error('One.patchRemove: missing model for remove'));
+ }
+ if (!_.isArray(relateds)) {
+ relateds = [relateds];
+ }
+ collection = this._ensureCollection(model);
+ for (_j = 0, _len1 = relateds.length; _j < _len1; _j++) {
+ related = relateds[_j];
+ _ref = collection.models;
+ for (_k = 0, _len2 = _ref.length; _k < _len2; _k++) {
+ current_related_model = _ref[_k];
+ if (Utils.dataIsSameModel(current_related_model, related)) {
+ collection.remove(current_related_model);
+ break;
+ }
+ }
+ }
+ related_ids = (function() {
+ var _l, _len3, _results;
+ _results = [];
+ for (_l = 0, _len3 = relateds.length; _l < _len3; _l++) {
+ related = relateds[_l];
+ _results.push(Utils.dataId(related));
+ }
+ return _results;
+ })();
+ if (this.join_table) {
+ query = {};
+ query[this.join_key] = model.id;
+ query[this.reverse_relation.join_key] = {
+ $in: related_ids
+ };
+ return this.join_table.destroy(query, callback);
+ } else {
+ query = {};
+ query[this.reverse_relation.foreign_key] = model.id;
+ query.id = {
+ $in: related_ids
+ };
+ return this.reverse_model_type.cursor(query).toJSON(function(err, json) {
+ var queue, related_json, _fn, _l, _len3;
+ if (err) {
+ return callback(err);
+ }
+ queue = new Queue(1);
+ _fn = function(related_json) {
+ return queue.defer(function(callback) {
+ related_json[_this.reverse_relation.foreign_key] = null;
+ return Utils.modelJSONSave(related_json, _this.reverse_model_type, callback);
+ });
+ };
+ for (_l = 0, _len3 = json.length; _l < _len3; _l++) {
+ related_json = json[_l];
+ _fn(related_json);
+ }
+ return queue.await(callback);
+ });
+ }
+ };
+
+ Many.prototype.cursor = function(model, key, query) {
+ var json;
+ json = Utils.isModel(model) ? model.attributes : model;
+ (query = _.clone(query || {}))[this.join_table ? this.join_key : this.reverse_relation.foreign_key] = json[this.model_type.prototype.idAttribute];
+ if (key === this.virtual_id_accessor) {
+ (query.$values || (query.$values = [])).push('id');
+ }
+ return this.reverse_model_type.cursor(query);
+ };
+
+ Many.prototype._bindBacklinks = function(model) {
+ var collection, events, method, _i, _len, _ref,
+ _this = this;
+ if ((collection = model.attributes[this.key]) instanceof this.collection_type) {
+ return collection;
+ }
+ collection = model.attributes[this.key] = new this.collection_type();
+ if (!this.reverse_relation) {
+ return collection;
+ }
+ events = Utils.set(collection, 'events', {});
+ events.add = function(related_model) {
+ var current_model, is_current;
+ if (_this.reverse_relation.add) {
+ return _this.reverse_relation.add(related_model, model);
+ } else {
+ current_model = related_model.get(_this.reverse_relation.key);
+ is_current = model.id && (Utils.dataId(current_model) === model.id);
+ if (!is_current || (is_current && !current_model.isLoaded())) {
+ return related_model.set(_this.reverse_relation.key, model);
+ }
+ }
+ };
+ events.remove = function(related_model) {
+ var current_model;
+ if (_this.reverse_relation.remove) {
+ return _this.reverse_relation.remove(related_model, model);
+ } else {
+ current_model = related_model.get(_this.reverse_relation.key);
+ if (Utils.dataId(current_model) === model.id) {
+ return related_model.set(_this.reverse_relation.key, null);
+ }
+ }
+ };
+ events.reset = function(collection, options) {
+ var added, changes, current_models, previous_models, related_model, _i, _j, _len, _len1, _ref, _results;
+ current_models = collection.models;
+ previous_models = options.previousModels || [];
+ changes = _.groupBy(previous_models, function(test) {
+ if (!!_.find(current_models, function(current_model) {
+ return current_model.id === test.id;
+ })) {
+ return 'kept';
+ } else {
+ return 'removed';
+ }
+ });
+ added = changes.kept ? _.select(current_models, function(test) {
+ return !_.find(changes.kept, function(keep_model) {
+ return keep_model.id === test.id;
+ });
+ }) : current_models;
+ if (changes.removed) {
+ _ref = changes.removed;
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ related_model = _ref[_i];
+ events.remove(related_model);
+ }
+ }
+ _results = [];
+ for (_j = 0, _len1 = added.length; _j < _len1; _j++) {
+ related_model = added[_j];
+ _results.push(events.add(related_model));
+ }
+ return _results;
+ };
+ _ref = ['add', 'remove', 'reset'];
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ method = _ref[_i];
+ collection.on(method, events[method]);
+ }
+ return collection;
+ };
+
+ Many.prototype._unbindBacklinks = function(model) {
+ var collection, events, method, _i, _len, _ref;
+ if (!(events = Utils.get(model, 'events'))) {
+ return;
+ }
+ Utils.unset(model, 'events');
+ collection = model.attributes[this.key];
+ collection.models.splice();
+ events = _.clone();
+ _ref = ['add', 'remove', 'reset'];
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ method = _ref[_i];
+ collection.off(method, events[method]);
+ events[method] = null;
+ }
+ };
+
+ Many.prototype._ensureCollection = function(model) {
+ return this._bindBacklinks(model);
+ };
+
+ Many.prototype._hasChanged = function(model) {
+ var collection, _i, _len, _ref;
+ return !!Utils.orSet(model, 'rel_dirty', {})[this.key] || model.hasChanged(this.key);
+ if (!this.reverse_relation) {
+ return false;
+ }
+ collection = this._ensureCollection(model);
+ _ref = model.models;
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ model = _ref[_i];
+ if (model.hasChanged(this.reverse_relation.foreign_key)) {
+ return true;
+ }
+ }
+ return false;
+ };
+
+ return Many;
+
+})(require('./relation'));
+
+});
+
+;require.register("backbone-orm/lib/relations/one", function(exports, require, module) {
+/*
+ backbone-orm.js 0.5.3
+ Copyright (c) 2013 Vidigami - https://github.com/vidigami/backbone-orm
+ License: MIT (http://www.opensource.org/licenses/mit-license.php)
+ Dependencies: Backbone.js, Underscore.js, Moment.js, and Inflection.js.
+*/
+
+var Backbone, One, Queue, Utils, inflection, _,
+ __hasProp = {}.hasOwnProperty,
+ __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
+
+_ = require('underscore');
+
+Backbone = require('backbone');
+
+inflection = require('inflection');
+
+Queue = require('../queue');
+
+Utils = require('../utils');
+
+module.exports = One = (function(_super) {
+ __extends(One, _super);
+
+ function One(model_type, key, options) {
+ var value;
+ this.model_type = model_type;
+ this.key = key;
+ for (key in options) {
+ value = options[key];
+ this[key] = value;
+ }
+ this.virtual_id_accessor || (this.virtual_id_accessor = "" + this.key + "_id");
+ if (!this.join_key) {
+ this.join_key = this.foreign_key || inflection.foreign_key(this.model_type.model_name);
+ }
+ if (!this.foreign_key) {
+ this.foreign_key = inflection.foreign_key(this.type === 'belongsTo' ? this.key : this.as || this.model_type.model_name);
+ }
+ }
+
+ One.prototype.initialize = function() {
+ this.reverse_relation = this._findOrGenerateReverseRelation(this);
+ if (this.embed && this.reverse_relation && this.reverse_relation.embed) {
+ throw new Error("Both relationship directions cannot embed (" + this.model_type.model_name + " and " + this.reverse_model_type.model_name + "). Choose one or the other.");
+ }
+ };
+
+ One.prototype.initializeModel = function(model) {
+ if (!model.isLoadedExists(this.key)) {
+ model.setLoaded(this.key, this.isEmbedded());
+ }
+ return this._bindBacklinks(model);
+ };
+
+ One.prototype.releaseModel = function(model) {
+ this._unbindBacklinks(model);
+ return delete model._orm;
+ };
+
+ One.prototype.set = function(model, key, value, options) {
+ var is_model, merge_into_existing, new_related_id, previous_related_id, previous_related_model;
+ if (!((key === this.key) || (key === this.virtual_id_accessor) || (key === this.foreign_key))) {
+ throw new Error("One.set: Unexpected key " + key + ". Expecting: " + this.key + " or " + this.virtual_id_accessor + " or " + this.foreign_key);
+ }
+ if (_.isArray(value)) {
+ throw new Error("One.set: cannot set an array for attribute " + this.key + " on " + this.model_type.model_name);
+ }
+ if (_.isUndefined(value)) {
+ value = null;
+ }
+ if (value === (previous_related_model = model.get(this.key))) {
+ return this;
+ }
+ is_model = Utils.isModel(value);
+ new_related_id = Utils.dataId(value);
+ previous_related_id = Utils.dataId(previous_related_model);
+ Utils.orSet(model, 'rel_dirty', {})[this.key] = true;
+ if ((previous_related_id !== new_related_id) || !model.isLoaded(this.key)) {
+ if ((is_model && (value.isLoaded())) && (new_related_id !== value)) {
+ model.setLoaded(this.key, true);
+ } else {
+ model.setLoaded(this.key, _.isNull(value));
+ }
+ }
+ if (value && !is_model) {
+ if (!(merge_into_existing = previous_related_id === new_related_id)) {
+ value = Utils.updateOrNew(value, this.reverse_model_type);
+ }
+ }
+ if (!merge_into_existing) {
+ Backbone.Model.prototype.set.call(model, this.key, value, options);
+ }
+ if (merge_into_existing) {
+ Utils.updateModel(previous_related_model, value);
+ } else if ((value === null) && this.reverse_relation && (this.reverse_relation.type === 'hasOne' || this.reverse_relation.type === 'belongsTo')) {
+ if (!(this.embed || this.reverse_relation.embed)) {
+ if (model.isLoaded(this.key) && previous_related_model && (previous_related_model.get(this.reverse_relation.key) === model)) {
+ previous_related_model.set(this.reverse_relation.key, null);
+ }
+ }
+ }
+ return this;
+ };
+
+ One.prototype.get = function(model, key, callback) {
+ var is_loaded, result, returnValue,
+ _this = this;
+ if (!((key === this.key) || (key === this.virtual_id_accessor) || (key === this.foreign_key))) {
+ throw new Error("One.get: Unexpected key " + key + ". Expecting: " + this.key + " or " + this.virtual_id_accessor + " or " + this.foreign_key);
+ }
+ returnValue = function() {
+ var related_model;
+ if (!(related_model = model.attributes[_this.key])) {
+ return null;
+ }
+ if (key === _this.virtual_id_accessor) {
+ return related_model.id;
+ } else {
+ return related_model;
+ }
+ };
+ if (callback && !this.isVirtual() && !this.manual_fetch && !(is_loaded = model.isLoaded(this.key))) {
+ this.cursor(model, key).toJSON(function(err, json) {
+ var previous_related_model, related_model;
+ if (err) {
+ return callback(err);
+ }
+ if (key !== _this.virtual_id_accessor) {
+ model.setLoaded(_this.key, true);
+ }
+ previous_related_model = model.get(_this.key);
+ if (previous_related_model && (previous_related_model.id === (json != null ? json.id : void 0))) {
+ Utils.updateModel(previous_related_model, json);
+ } else {
+ related_model = json ? Utils.updateOrNew(json, _this.reverse_model_type) : null;
+ model.set(_this.key, related_model);
+ }
+ return callback(null, returnValue());
+ });
+ }
+ result = returnValue();
+ if (callback && (is_loaded || this.manual_fetch)) {
+ callback(null, result);
+ }
+ return result;
+ };
+
+ One.prototype.save = function(model, callback) {
+ var related_model;
+ if (!this._hasChanged(model)) {
+ return callback();
+ }
+ delete Utils.orSet(model, 'rel_dirty', {})[this.key];
+ if (!(related_model = model.attributes[this.key])) {
+ return callback();
+ }
+ return this._saveRelated(model, [related_model], callback);
+ };
+
+ One.prototype.patchAdd = function(model, related, callback) {
+ var found_related, related_id,
+ _this = this;
+ if (!model.id) {
+ return callback(new Error("One.patchAdd: model has null id for: " + this.key));
+ }
+ if (!related) {
+ return callback(new Error("One.patchAdd: missing model for: " + this.key));
+ }
+ if (_.isArray(related)) {
+ return callback(new Error("One.patchAdd: should be provided with one model only for key: " + this.key));
+ }
+ if (!(related_id = Utils.dataId(related))) {
+ return callback(new Error("One.patchAdd: cannot add a new model. Please save first."));
+ }
+ if (this.reverse_model_type.cache && !Utils.isModel(related)) {
+ if (found_related = this.reverse_model_type.cache.get(related_id)) {
+ Utils.updateModel(found_related, related);
+ related = found_related;
+ }
+ }
+ model.set(this.key, related);
+ if (this.type === 'belongsTo') {
+ return this.model_type.cursor({
+ id: model.id,
+ $one: true
+ }).toJSON(function(err, model_json) {
+ if (err) {
+ return callback(err);
+ }
+ if (!model_json) {
+ return callback(new Error("Failed to fetch model with id: " + model.id));
+ }
+ model_json[_this.foreign_key] = related_id;
+ return model.save(model_json, callback);
+ });
+ } else {
+ return this.cursor(model, this.key).toJSON(function(err, current_related_json) {
+ var queue;
+ if (err) {
+ return callback(err);
+ }
+ if (current_related_json && (related_id === current_related_json[_this.reverse_model_type.prototype.idAttribute])) {
+ return callback();
+ }
+ queue = new Queue(1);
+ if (current_related_json) {
+ queue.defer(function(callback) {
+ return _this.patchRemove(model, current_related_json, callback);
+ });
+ }
+ queue.defer(function(callback) {
+ var query, related_json;
+ if (Utils.isModel(related)) {
+ if (related.isLoaded()) {
+ related_json = related.toJSON();
+ }
+ } else if (related_id !== related) {
+ related_json = related;
+ }
+ if (related_json) {
+ related_json[_this.reverse_relation.foreign_key] = model.id;
+ return Utils.modelJSONSave(related_json, _this.reverse_model_type, callback);
+ } else {
+ query = {
+ $one: true
+ };
+ query.id = related_id;
+ return _this.reverse_model_type.cursor(query).toJSON(function(err, related_json) {
+ if (err) {
+ return callback(err);
+ }
+ if (!related_json) {
+ return callback();
+ }
+ related_json[_this.reverse_relation.foreign_key] = model.id;
+ return Utils.modelJSONSave(related_json, _this.reverse_model_type, callback);
+ });
+ }
+ });
+ return queue.await(callback);
+ });
+ }
+ };
+
+ One.prototype.patchRemove = function(model, relateds, callback) {
+ var current_related_model, related, related_ids, _i, _len,
+ _this = this;
+ if (arguments.length === 2) {
+ callback = relateds;
+ relateds = void 0;
+ }
+ if (!model.id) {
+ return callback(new Error("One.patchRemove: model has null id for: " + this.key));
+ }
+ if (arguments.length === 2) {
+ if (!this.reverse_relation) {
+ return callback();
+ }
+ if (Utils.isModel(model)) {
+ delete Utils.orSet(model, 'rel_dirty', {})[this.key];
+ }
+ this.cursor(model, this.key).toJSON(function(err, related_json) {
+ if (err) {
+ return callback(err);
+ }
+ if (!related_json) {
+ return callback();
+ }
+ related_json[_this.reverse_relation.foreign_key] = null;
+ return Utils.modelJSONSave(related_json, _this.reverse_model_type, callback);
+ });
+ return;
+ }
+ if (this.isEmbedded()) {
+ return callback(new Error('One.patchRemove: embedded relationships are not supported'));
+ }
+ if (!relateds) {
+ return callback(new Error('One.patchRemove: missing model for remove'));
+ }
+ if (!_.isArray(relateds)) {
+ relateds = [relateds];
+ }
+ if (current_related_model = model.get(this.key)) {
+ for (_i = 0, _len = relateds.length; _i < _len; _i++) {
+ related = relateds[_i];
+ if (Utils.dataIsSameModel(current_related_model, related)) {
+ model.set(this.key, null);
+ break;
+ }
+ }
+ }
+ related_ids = (function() {
+ var _j, _len1, _results;
+ _results = [];
+ for (_j = 0, _len1 = relateds.length; _j < _len1; _j++) {
+ related = relateds[_j];
+ _results.push(Utils.dataId(related));
+ }
+ return _results;
+ })();
+ if (this.type === 'belongsTo') {
+ return this.model_type.cursor({
+ id: model.id,
+ $one: true
+ }).toJSON(function(err, model_json) {
+ if (err) {
+ return callback(err);
+ }
+ if (!model_json) {
+ return callback();
+ }
+ if (!_.contains(related_ids, model_json[_this.foreign_key])) {
+ return callback();
+ }
+ model_json[_this.foreign_key] = null;
+ return Utils.modelJSONSave(model_json, _this.model_type, callback);
+ });
+ } else {
+ return this.cursor(model, this.key).toJSON(function(err, related_json) {
+ if (err) {
+ return callback(err);
+ }
+ if (!related_json) {
+ return callback();
+ }
+ if (!_.contains(related_ids, related_json[_this.reverse_model_type.prototype.idAttribute])) {
+ return callback();
+ }
+ related_json[_this.reverse_relation.foreign_key] = null;
+ return Utils.modelJSONSave(related_json, _this.reverse_model_type, callback);
+ });
+ }
+ };
+
+ One.prototype.appendJSON = function(json, model) {
+ var json_key, related_model;
+ if (this.isVirtual()) {
+ return;
+ }
+ json_key = this.embed ? this.key : this.foreign_key;
+ if (!(related_model = model.attributes[this.key])) {
+ if (this.embed || this.type === 'belongsTo') {
+ json[json_key] = null;
+ }
+ return;
+ }
+ if (this.embed) {
+ return json[json_key] = related_model.toJSON();
+ }
+ if (this.type === 'belongsTo') {
+ return json[json_key] = related_model.id;
+ }
+ };
+
+ One.prototype.cursor = function(model, key, query) {
+ var _ref;
+ query = _.extend({
+ $one: true
+ }, query || {});
+ if (Utils.isModel(model)) {
+ if (this.type === 'belongsTo') {
+ if (!(query.id = (_ref = model.attributes[this.key]) != null ? _ref.id : void 0)) {
+ query.$zero = true;
+ delete query.id;
+ }
+ } else {
+ if (!model.id) {
+ throw new Error('Cannot create cursor for non-loaded model');
+ }
+ query[this.reverse_relation.foreign_key] = model.id;
+ }
+ } else {
+ if (this.type === 'belongsTo') {
+ if (!(query.id = model[this.foreign_key])) {
+ query.$zero = true;
+ delete query.id;
+ }
+ } else {
+ if (!model.id) {
+ throw new Error('Cannot create cursor for non-loaded model');
+ }
+ query[this.reverse_relation.foreign_key] = model.id;
+ }
+ }
+ if (key === this.virtual_id_accessor) {
+ query.$values = ['id'];
+ }
+ return this.reverse_model_type.cursor(query);
+ };
+
+ One.prototype._bindBacklinks = function(model) {
+ var events, related_model, setBacklink,
+ _this = this;
+ if (!this.reverse_relation) {
+ return;
+ }
+ events = Utils.set(model, 'events', {});
+ setBacklink = function(related_model) {
+ if (_this.reverse_relation.add) {
+ return _this.reverse_relation.add(related_model, model);
+ } else {
+ return related_model.set(_this.reverse_relation.key, model);
+ }
+ };
+ events.change = function(model) {
+ var current_model, previous_related_model, related_model;
+ related_model = model.get(_this.key);
+ previous_related_model = model.previous(_this.key);
+ if (Utils.dataId(related_model) === Utils.dataId(previous_related_model)) {
+ return;
+ }
+ if (previous_related_model && (_this.reverse_relation && _this.reverse_relation.type !== 'belongsTo')) {
+ if (_this.reverse_relation.remove) {
+ if (!_this.isVirtual() || !related_model) {
+ _this.reverse_relation.remove(previous_related_model, model);
+ }
+ } else {
+ current_model = previous_related_model.get(_this.reverse_relation.key);
+ if (Utils.dataId(current_model) === model.id) {
+ previous_related_model.set(_this.reverse_relation.key, null);
+ }
+ }
+ }
+ if (related_model) {
+ return setBacklink(related_model);
+ }
+ };
+ model.on("change:" + this.key, events.change);
+ if (related_model = model.get(this.key)) {
+ setBacklink(related_model);
+ } else {
+ model.attributes[this.key] = null;
+ }
+ return model;
+ };
+
+ One.prototype._unbindBacklinks = function(model) {
+ var events;
+ if (!(events = Utils.get(model, 'events'))) {
+ return;
+ }
+ Utils.unset(model, 'events');
+ model.attributes[this.key] = null;
+ model.off("change:" + this.key, events.change);
+ events.change = null;
+ };
+
+ One.prototype._hasChanged = function(model) {
+ var related_model;
+ return !!Utils.orSet(model, 'rel_dirty', {})[this.key] || model.hasChanged(this.key);
+ if (!this.reverse_relation) {
+ return false;
+ }
+ if (!(related_model = model.attributes[this.key])) {
+ return false;
+ }
+ return related_model.hasChanged(this.reverse_relation.foreign_key);
+ };
+
+ return One;
+
+})(require('./relation'));
+
+});
+
+;require.register("backbone-orm/lib/relations/relation", function(exports, require, module) {
+/*
+ backbone-orm.js 0.5.3
+ Copyright (c) 2013 Vidigami - https://github.com/vidigami/backbone-orm
+ License: MIT (http://www.opensource.org/licenses/mit-license.php)
+ Dependencies: Backbone.js, Underscore.js, Moment.js, and Inflection.js.
+*/
+
+var Backbone, Queue, Relation, Utils, inflection, _;
+
+_ = require('underscore');
+
+Backbone = require('backbone');
+
+Queue = require('../queue');
+
+inflection = require('inflection');
+
+Utils = require('../utils');
+
+module.exports = Relation = (function() {
+ function Relation() {}
+
+ Relation.prototype.isEmbedded = function() {
+ return !!(this.embed || (this.reverse_relation && this.reverse_relation.embed));
+ };
+
+ Relation.prototype.isVirtual = function() {
+ return !!(this.virtual || (this.reverse_relation && this.reverse_relation.virtual));
+ };
+
+ Relation.prototype.findOrGenerateJoinTable = function() {
+ var join_table;
+ if (join_table = this.join_table || this.reverse_relation.join_table) {
+ return join_table;
+ }
+ return this.model_type.schema().generateJoinTable(this);
+ };
+
+ Relation.prototype._findOrGenerateReverseRelation = function() {
+ var key_root, model_type, reverse_model_type, reverse_relation;
+ model_type = this.model_type;
+ reverse_model_type = this.reverse_model_type;
+ if (!_.isFunction(reverse_model_type.schema)) {
+ reverse_model_type.sync = model_type.createSync(reverse_model_type);
+ }
+ key_root = this.as || inflection.underscore(model_type.model_name);
+ reverse_relation = reverse_model_type.relation(key_root);
+ if (!reverse_relation) {
+ reverse_relation = reverse_model_type.relation(inflection.singularize(key_root));
+ }
+ if (!reverse_relation) {
+ reverse_relation = reverse_model_type.relation(inflection.pluralize(key_root));
+ }
+ if (!reverse_relation && (this.type !== 'belongsTo')) {
+ reverse_relation = reverse_model_type.schema().generateBelongsTo(model_type);
+ }
+ if (reverse_relation && !reverse_relation.reverse_relation) {
+ reverse_relation.reverse_relation = this;
+ }
+ return reverse_relation;
+ };
+
+ Relation.prototype._saveRelated = function(model, related_models, callback) {
+ var _this = this;
+ if (this.embed || !this.reverse_relation || (this.type === 'belongsTo')) {
+ return callback();
+ }
+ if (this.isVirtual()) {
+ return callback();
+ }
+ return this.cursor(model, this.key).toJSON(function(err, json) {
+ var added_id, added_ids, changes, queue, related_id, related_ids, related_json, related_model, test, _fn, _fn1, _fn2, _i, _j, _k, _len, _len1, _len2, _ref;
+ if (err) {
+ return callback(err);
+ }
+ if (!_.isArray(json)) {
+ json = (json ? [json] : []);
+ }
+ queue = new Queue(1);
+ related_ids = _.pluck(related_models, 'id');
+ changes = _.groupBy(json, function(test) {
+ if (_.contains(related_ids, test.id)) {
+ return 'kept';
+ } else {
+ return 'removed';
+ }
+ });
+ added_ids = changes.kept ? _.difference(related_ids, (function() {
+ var _i, _len, _ref, _results;
+ _ref = changes.kept;
+ _results = [];
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ test = _ref[_i];
+ _results.push(test.id);
+ }
+ return _results;
+ })()) : related_ids;
+ if (changes.removed) {
+ if (_this.join_table) {
+ queue.defer(function(callback) {
+ var query, related_json;
+ query = {};
+ query[_this.reverse_relation.join_key] = {
+ $in: (function() {
+ var _i, _len, _ref, _results;
+ _ref = changes.removed;
+ _results = [];
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ related_json = _ref[_i];
+ _results.push(related_json[this.reverse_model_type.prototype.idAttribute]);
+ }
+ return _results;
+ }).call(_this)
+ };
+ return _this.join_table.destroy(query, callback);
+ });
+ } else {
+ _ref = changes.removed;
+ _fn = function(related_json) {
+ return queue.defer(function(callback) {
+ related_json[_this.reverse_relation.foreign_key] = null;
+ return Utils.modelJSONSave(related_json, _this.reverse_model_type, callback);
+ });
+ };
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ related_json = _ref[_i];
+ _fn(related_json);
+ }
+ }
+ }
+ if (added_ids.length) {
+ if (_this.join_table) {
+ _fn1 = function(related_id) {
+ return queue.defer(function(callback) {
+ var attributes, join;
+ attributes = {};
+ attributes[_this.foreign_key] = model.id;
+ attributes[_this.reverse_relation.foreign_key] = related_id;
+ join = new _this.join_table(attributes);
+ return join.save(callback);
+ });
+ };
+ for (_j = 0, _len1 = added_ids.length; _j < _len1; _j++) {
+ related_id = added_ids[_j];
+ _fn1(related_id);
+ }
+ } else {
+ _fn2 = function(related_model) {
+ return queue.defer(function(callback) {
+ return related_model.save(function(err, saved_model) {
+ var cache;
+ if (!err && (cache = _this.reverse_model_type.cache)) {
+ cache.set(saved_model.id, saved_model);
+ }
+ return callback(err);
+ });
+ });
+ };
+ for (_k = 0, _len2 = added_ids.length; _k < _len2; _k++) {
+ added_id = added_ids[_k];
+ related_model = _.find(related_models, function(test) {
+ return test.id === added_id;
+ });
+ if (!_this.reverse_relation._hasChanged(related_model)) {
+ continue;
+ }
+ _fn2(related_model);
+ }
+ }
+ }
+ return queue.await(callback);
+ });
+ };
+
+ return Relation;
+
+})();
+
+});
+
+;require.register("backbone-orm/lib/schema", function(exports, require, module) {
+/*
+ backbone-orm.js 0.5.3
+ Copyright (c) 2013 Vidigami - https://github.com/vidigami/backbone-orm
+ License: MIT (http://www.opensource.org/licenses/mit-license.php)
+ Dependencies: Backbone.js, Underscore.js, Moment.js, and Inflection.js.
+*/
+
+var Backbone, DatabaseURL, Many, One, RELATION_VARIANTS, Schema, inflection, _,
+ __hasProp = {}.hasOwnProperty,
+ __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
+
+_ = require('underscore');
+
+Backbone = require('backbone');
+
+inflection = require('inflection');
+
+One = require('./relations/one');
+
+Many = require('./relations/many');
+
+DatabaseURL = require('./database_url');
+
+RELATION_VARIANTS = {
+ 'hasOne': 'hasOne',
+ 'has_one': 'hasOne',
+ 'HasOne': 'hasOne',
+ 'belongsTo': 'belongsTo',
+ 'belongs_to': 'belongsTo',
+ 'BelongsTo': 'belongsTo',
+ 'hasMany': 'hasMany',
+ 'has_many': 'hasMany',
+ 'HasMany': 'hasMany'
+};
+
+module.exports = Schema = (function() {
+ function Schema(model_type) {
+ this.model_type = model_type;
+ this.raw = _.clone(_.result(new this.model_type(), 'schema') || {});
+ this.fields = {};
+ this.relations = {};
+ this.virtual_accessors = {};
+ }
+
+ Schema.prototype.initialize = function() {
+ var info, key, relation, _ref, _ref1;
+ if (this.is_initialized) {
+ return;
+ }
+ this.is_initialized = true;
+ _ref = this.raw;
+ for (key in _ref) {
+ info = _ref[key];
+ this._parseField(key, info);
+ }
+ _ref1 = this.relations;
+ for (key in _ref1) {
+ relation = _ref1[key];
+ relation.initialize();
+ }
+ };
+
+ Schema.prototype.relation = function(key) {
+ return this.relations[key] || this.virtual_accessors[key];
+ };
+
+ Schema.prototype.reverseRelation = function(reverse_key) {
+ var key, relation, _ref;
+ _ref = this.relations;
+ for (key in _ref) {
+ relation = _ref[key];
+ if (relation.reverse_relation && (relation.reverse_relation.join_key === reverse_key)) {
+ return relation.reverse_relation;
+ }
+ }
+ return null;
+ };
+
+ Schema.prototype.allRelations = function() {
+ var key, related_model_types, relation, _ref;
+ related_model_types = [];
+ _ref = this.relations;
+ for (key in _ref) {
+ relation = _ref[key];
+ related_model_types.push(relation.reverse_model_type);
+ if (relation.join_table) {
+ related_model_types.push(relation.join_table);
+ }
+ }
+ return related_model_types;
+ };
+
+ Schema.prototype.generateBelongsTo = function(reverse_model_type) {
+ var key, relation;
+ key = inflection.underscore(reverse_model_type.model_name);
+ if (relation = this.relations[key]) {
+ return relation;
+ }
+ if (this.raw[key]) {
+ relation = this._parseField(key, this.raw[key]);
+ relation.initialize();
+ return relation;
+ }
+ relation = this._parseField(key, this.raw[key] = [
+ 'belongsTo', reverse_model_type, {
+ manual_fetch: true
+ }
+ ]);
+ relation.initialize();
+ return relation;
+ };
+
+ Schema.joinTableURL = function(relation) {
+ var model_name1, model_name2;
+ model_name1 = inflection.pluralize(inflection.underscore(relation.model_type.model_name));
+ model_name2 = inflection.pluralize(inflection.underscore(relation.reverse_relation.model_type.model_name));
+ if (model_name1.localeCompare(model_name2) < 0) {
+ return "" + model_name1 + "_" + model_name2;
+ } else {
+ return "" + model_name2 + "_" + model_name1;
+ }
+ };
+
+ Schema.prototype.generateJoinTable = function(relation) {
+ var JoinTable, name, schema, url, _ref, _ref1;
+ schema = {};
+ schema[relation.join_key] = [
+ 'Integer', {
+ indexed: true
+ }
+ ];
+ schema[relation.reverse_relation.join_key] = [
+ 'Integer', {
+ indexed: true
+ }
+ ];
+ url = Schema.joinTableURL(relation);
+ name = inflection.pluralize(inflection.classify(url));
+ try {
+ JoinTable = (function(_super) {
+ __extends(JoinTable, _super);
+
+ function JoinTable() {
+ _ref = JoinTable.__super__.constructor.apply(this, arguments);
+ return _ref;
+ }
+
+ JoinTable.prototype.model_name = name;
+
+ JoinTable.prototype.urlRoot = "" + ((new DatabaseURL(_.result(relation.model_type.prototype, 'url'))).format({
+ exclude_table: true
+ })) + "/" + url;
+
+ JoinTable.prototype.schema = schema;
+
+ JoinTable.prototype.sync = relation.model_type.createSync(JoinTable);
+
+ return JoinTable;
+
+ })(Backbone.Model);
+ } catch (_error) {
+ JoinTable = (function(_super) {
+ __extends(JoinTable, _super);
+
+ function JoinTable() {
+ _ref1 = JoinTable.__super__.constructor.apply(this, arguments);
+ return _ref1;
+ }
+
+ JoinTable.prototype.model_name = name;
+
+ JoinTable.prototype.urlRoot = "/" + url;
+
+ JoinTable.prototype.schema = schema;
+
+ JoinTable.prototype.sync = relation.model_type.createSync(JoinTable);
+
+ return JoinTable;
+
+ })(Backbone.Model);
+ }
+ return JoinTable;
+ };
+
+ Schema.prototype.allColumns = function() {
+ var columns, key, relation, _ref;
+ columns = _.keys(this.fields);
+ _ref = this.relations;
+ for (key in _ref) {
+ relation = _ref[key];
+ if (relation.type === 'belongsTo') {
+ columns.push(relation.foreign_key);
+ }
+ }
+ return columns;
+ };
+
+ Schema.prototype._parseField = function(key, info) {
+ var options, relation, type;
+ options = this._fieldInfoToOptions(_.isFunction(info) ? info() : info);
+ if (!options.type) {
+ return this.fields[key] = options;
+ }
+ if (!(type = RELATION_VARIANTS[options.type])) {
+ if (!_.isString(options.type)) {
+ throw new Error("Unexpected type name is not a string: " + (util.inspect(options)));
+ }
+ return this.fields[key] = options;
+ }
+ options.type = type;
+ relation = this.relations[key] = type === 'hasMany' ? new Many(this.model_type, key, options) : new One(this.model_type, key, options);
+ if (relation.virtual_id_accessor) {
+ this.virtual_accessors[relation.virtual_id_accessor] = relation;
+ }
+ if (type === 'belongsTo') {
+ this.virtual_accessors[relation.foreign_key] = relation;
+ }
+ return relation;
+ };
+
+ Schema.prototype._fieldInfoToOptions = function(options) {
+ var result;
+ if (_.isString(options)) {
+ return {
+ type: options
+ };
+ }
+ if (!_.isArray(options)) {
+ return options;
+ }
+ result = {};
+ if (_.isString(options[0])) {
+ result.type = options[0];
+ options = options.slice(1);
+ if (options.length === 0) {
+ return result;
+ }
+ }
+ if (_.isFunction(options[0])) {
+ result.reverse_model_type = options[0];
+ options = options.slice(1);
+ }
+ if (options.length > 1) {
+ throw new Error("Unexpected field options array: " + (util.inspect(options)));
+ }
+ if (options.length === 1) {
+ _.extend(result, options[0]);
+ }
+ return result;
+ };
+
+ return Schema;
+
+})();
+
+});
+
+;require.register("backbone-orm/lib/utils", function(exports, require, module) {
+/*
+ backbone-orm.js 0.5.3
+ Copyright (c) 2013 Vidigami - https://github.com/vidigami/backbone-orm
+ License: MIT (http://www.opensource.org/licenses/mit-license.php)
+ Dependencies: Backbone.js, Underscore.js, Moment.js, and Inflection.js.
+*/
+
+var Backbone, DatabaseURL, JSONUtils, Queue, S4, URL, Utils, inflection, modelExtensions, _,
+ __hasProp = {}.hasOwnProperty,
+ __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
+
+URL = require('url');
+
+DatabaseURL = require('./database_url');
+
+Backbone = require('backbone');
+
+_ = require('underscore');
+
+inflection = require('inflection');
+
+Queue = require('./queue');
+
+JSONUtils = require('./json_utils');
+
+modelExtensions = null;
+
+S4 = function() {
+ return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
+};
+
+module.exports = Utils = (function() {
+ function Utils() {}
+
+ Utils.resetSchemas = function(model_types, options, callback) {
+ var failed_schemas, model_type, queue, _fn, _i, _j, _len, _len1, _ref;
+ if (arguments.length === 2) {
+ _ref = [{}, options], options = _ref[0], callback = _ref[1];
+ }
+ for (_i = 0, _len = model_types.length; _i < _len; _i++) {
+ model_type = model_types[_i];
+ model_type.schema();
+ }
+ failed_schemas = [];
+ queue = new Queue(1);
+ _fn = function(model_type) {
+ return queue.defer(function(callback) {
+ return model_type.resetSchema(options, function(err) {
+ if (err) {
+ failed_schemas.push(model_type.model_name);
+ console.log("Error when dropping schema for " + model_type.model_name + ". " + err);
+ }
+ return callback();
+ });
+ });
+ };
+ for (_j = 0, _len1 = model_types.length; _j < _len1; _j++) {
+ model_type = model_types[_j];
+ _fn(model_type);
+ }
+ return queue.await(function(err) {
+ if (options.verbose) {
+ console.log("" + (model_types.length - failed_schemas.length) + " schemas dropped.");
+ }
+ if (failed_schemas.length) {
+ return callback(new Error("Failed to migrate schemas: " + (failed_schemas.join(', '))));
+ }
+ return callback();
+ });
+ };
+
+ Utils.guid = function() {
+ return S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4();
+ };
+
+ Utils.inspect = function(obj) {
+ var err;
+ try {
+ return JSON.stringify(obj);
+ } catch (_error) {
+ err = _error;
+ return "inspect: " + err;
+ }
+ };
+
+ Utils.bbCallback = function(callback) {
+ return {
+ success: (function(model, resp, options) {
+ return callback(null, model, resp, options);
+ }),
+ error: (function(model, resp, options) {
+ return callback(resp || new Error('Backbone call failed'), model, resp, options);
+ })
+ };
+ };
+
+ Utils.wrapOptions = function(options, callback) {
+ if (options == null) {
+ options = {};
+ }
+ if (_.isFunction(options)) {
+ options = Utils.bbCallback(options);
+ }
+ return _.defaults(Utils.bbCallback(function(err, model, resp, modified_options) {
+ return callback(err, model, resp, options);
+ }), options);
+ };
+
+ Utils.isModel = function(obj) {
+ return obj && obj.attributes && ((obj instanceof Backbone.Model) || (obj.parse && obj.fetch));
+ };
+
+ Utils.isCollection = function(obj) {
+ return obj && obj.models && ((obj instanceof Backbone.Collection) || (obj.reset && obj.fetch));
+ };
+
+ Utils.get = function(model, key, default_value) {
+ model._orm || (model._orm = {});
+ if (model._orm.hasOwnProperty(key)) {
+ return model._orm[key];
+ } else {
+ return default_value;
+ }
+ };
+
+ Utils.set = function(model, key, value) {
+ model._orm || (model._orm = {});
+ model._orm[key] = value;
+ return model._orm[key];
+ };
+
+ Utils.orSet = function(model, key, value) {
+ model._orm || (model._orm = {});
+ if (!model._orm.hasOwnProperty(key)) {
+ model._orm[key] = value;
+ }
+ return model._orm[key];
+ };
+
+ Utils.unset = function(model, key) {
+ model._orm || (model._orm = {});
+ return delete model._orm[key];
+ };
+
+ Utils.findOrGenerateModelName = function(model_type) {
+ var model_name, url;
+ if (model_type.prototype.model_name) {
+ return model_type.prototype.model_name;
+ }
+ if (url = _.result(model_type.prototype, 'url')) {
+ if (model_name = (new DatabaseURL(url)).modelName()) {
+ return model_name;
+ }
+ }
+ if (model_type.name) {
+ return model_type.name;
+ }
+ throw "Could not find or generate model name for " + model_type;
+ };
+
+ Utils.configureCollectionModelType = function(type, sync) {
+ var ORMModel, modelURL, model_type, _ref;
+ modelURL = function() {
+ var url, url_parts;
+ url = _.result(this.collection || type.prototype, 'url');
+ if (!this.isNew()) {
+ url_parts = URL.parse(url);
+ url_parts.pathname = "" + url_parts.pathname + "/encodeURIComponent(@id)";
+ url = URL.format(url_parts);
+ }
+ return url;
+ };
+ model_type = type.prototype.model;
+ if (!model_type || (model_type === Backbone.Model)) {
+ ORMModel = (function(_super) {
+ __extends(ORMModel, _super);
+
+ function ORMModel() {
+ _ref = ORMModel.__super__.constructor.apply(this, arguments);
+ return _ref;
+ }
+
+ ORMModel.prototype.url = modelURL;
+
+ ORMModel.prototype.sync = sync(ORMModel);
+
+ return ORMModel;
+
+ })(Backbone.Model);
+ return type.prototype.model = ORMModel;
+ } else if (model_type.prototype.sync === Backbone.Model.prototype.sync) {
+ model_type.prototype.url = modelURL;
+ model_type.prototype.sync = sync(model_type);
+ }
+ return model_type;
+ };
+
+ Utils.configureModelType = function(type) {
+ if (!modelExtensions) {
+ modelExtensions = require('./extensions/model');
+ }
+ return modelExtensions(type);
+ };
+
+ Utils.patchRemoveByJSON = function(model_type, model_json, callback) {
+ var key, queue, relation, schema, _fn, _i, _len;
+ if (!(schema = model_type.schema())) {
+ return callback();
+ }
+ queue = new Queue(1);
+ _fn = function(relation) {
+ return queue.defer(function(callback) {
+ return relation.patchRemove(model_json, callback);
+ });
+ };
+ for (relation = _i = 0, _len = schema.length; _i < _len; relation = ++_i) {
+ key = schema[relation];
+ _fn(relation);
+ }
+ return queue.await(callback);
+ };
+
+ Utils.presaveBelongsToRelationships = function(model, callback) {
+ var key, queue, related_model, related_models, relation, schema, value, _fn, _i, _len, _ref,
+ _this = this;
+ if (!model.schema) {
+ return callback();
+ }
+ queue = new Queue(1);
+ schema = model.schema();
+ _ref = schema.relations;
+ for (key in _ref) {
+ relation = _ref[key];
+ if (relation.type !== 'belongsTo' || relation.isVirtual() || !(value = model.get(key))) {
+ continue;
+ }
+ related_models = value.models ? value.models : [value];
+ _fn = function(related_model) {
+ return queue.defer(function(callback) {
+ return related_model.save(callback);
+ });
+ };
+ for (_i = 0, _len = related_models.length; _i < _len; _i++) {
+ related_model = related_models[_i];
+ if (related_model.id) {
+ continue;
+ }
+ _fn(related_model);
+ }
+ }
+ return queue.await(callback);
+ };
+
+ Utils.dataId = function(data) {
+ if (_.isObject(data)) {
+ return data.id;
+ } else {
+ return data;
+ }
+ };
+
+ Utils.dataIsSameModel = function(data1, data2) {
+ if (Utils.dataId(data1) || Utils.dataId(data2)) {
+ return Utils.dataId(data1) === Utils.dataId(data2);
+ }
+ return _.isEqual(data1, data2);
+ };
+
+ Utils.dataToModel = function(data, model_type) {
+ var attributes, item, model;
+ if (!data) {
+ return null;
+ }
+ if (_.isArray(data)) {
+ return (function() {
+ var _i, _len, _results;
+ _results = [];
+ for (_i = 0, _len = data.length; _i < _len; _i++) {
+ item = data[_i];
+ _results.push(Utils.dataToModel(item, model_type));
+ }
+ return _results;
+ })();
+ }
+ if (Utils.isModel(data)) {
+ model = data;
+ } else if (Utils.dataId(data) !== data) {
+ model = new model_type(model_type.prototype.parse(data));
+ } else {
+ (attributes = {})[model_type.prototype.idAttribute] = data;
+ model = new model_type(attributes);
+ model.setLoaded(false);
+ }
+ return model;
+ };
+
+ Utils.updateModel = function(model, data) {
+ if (!data || (model === data) || data._orm_needs_load) {
+ return model;
+ }
+ if (Utils.isModel(data)) {
+ data = data.toJSON();
+ }
+ if (Utils.dataId(data) !== data) {
+ model.setLoaded(true);
+ model.set(data);
+ }
+ return model;
+ };
+
+ Utils.updateOrNew = function(data, model_type) {
+ var cache, id, model;
+ if ((cache = model_type.cache) && (id = Utils.dataId(data))) {
+ if (model = cache.get(id)) {
+ Utils.updateModel(model, data);
+ }
+ }
+ if (!model) {
+ model = Utils.isModel(data) ? data : Utils.dataToModel(data, model_type);
+ if (model && cache) {
+ cache.set(model.id, model);
+ }
+ }
+ return model;
+ };
+
+ Utils.modelJSONSave = function(model_json, model_type, callback) {
+ var model,
+ _this = this;
+ model = new Backbone.Model(model_json);
+ model._orm_never_cache = true;
+ model.urlRoot = function() {
+ var e, url;
+ try {
+ url = _.result(model_type.prototype, 'url');
+ } catch (_error) {
+ e = _error;
+ }
+ return url;
+ };
+ return model_type.prototype.sync('update', model, Utils.bbCallback(callback));
+ };
+
+ Utils.isSorted = function(models, fields) {
+ var last_model, model, _i, _len;
+ fields = _.uniq(fields);
+ for (_i = 0, _len = models.length; _i < _len; _i++) {
+ model = models[_i];
+ if (last_model && this.fieldCompare(last_model, model, fields) === 1) {
+ return false;
+ }
+ last_model = model;
+ }
+ return true;
+ };
+
+ Utils.fieldCompare = function(model, other_model, fields) {
+ var desc, field;
+ field = fields[0];
+ if (_.isArray(field)) {
+ field = field[0];
+ }
+ if (field.charAt(0) === '-') {
+ field = field.substr(1);
+ desc = true;
+ }
+ if (model.get(field) === other_model.get(field)) {
+ if (fields.length > 1) {
+ return this.fieldCompare(model, other_model, fields.splice(1));
+ } else {
+ return 0;
+ }
+ }
+ if (desc) {
+ if (model.get(field) < other_model.get(field)) {
+ return 1;
+ } else {
+ return -1;
+ }
+ } else {
+ if (model.get(field) > other_model.get(field)) {
+ return 1;
+ } else {
+ return -1;
+ }
+ }
+ };
+
+ Utils.jsonFieldCompare = function(model, other_model, fields) {
+ var desc, field;
+ field = fields[0];
+ if (_.isArray(field)) {
+ field = field[0];
+ }
+ if (field.charAt(0) === '-') {
+ field = field.substr(1);
+ desc = true;
+ }
+ if (model[field] === other_model[field]) {
+ if (fields.length > 1) {
+ return this.jsonFieldCompare(model, other_model, fields.splice(1));
+ } else {
+ return 0;
+ }
+ }
+ if (desc) {
+ if (JSON.stringify(model[field]) < JSON.stringify(other_model[field])) {
+ return 1;
+ } else {
+ return -1;
+ }
+ } else {
+ if (JSON.stringify(model[field]) > JSON.stringify(other_model[field])) {
+ return 1;
+ } else {
+ return -1;
+ }
+ }
+ };
+
+ return Utils;
+
+})();
+
+});
+
+;
+if (typeof exports == 'object') {
+ module.exports = require('backbone-orm/lib/index');
+} else if (typeof define == 'function' && define.amd) {
+ define(['require', 'underscore', 'backbone', 'moment', 'inflection'], function(){ return require('backbone-orm/lib/index'); });
+} else {
+ this.BackboneORM = require('backbone-orm/lib/index');
+}
+}).call(this);
diff --git a/static/js/lib/backbone-relational.js b/static/js/lib/backbone-relational.js
new file mode 100644
index 0000000..3359bd1
--- /dev/null
+++ b/static/js/lib/backbone-relational.js
@@ -0,0 +1,3 @@
+/**
+ * Created by fergalm on 11/12/13.
+ */
diff --git a/static/js/lib/backbone.associations.js b/static/js/lib/backbone.associations.js
new file mode 100644
index 0000000..8ad9839
--- /dev/null
+++ b/static/js/lib/backbone.associations.js
@@ -0,0 +1,574 @@
+//
+// Backbone-associations.js 0.5.4
+//
+// (c) 2013 Dhruva Ray, Jaynti Kanani, Persistent Systems Ltd.
+// Backbone-associations may be freely distributed under the MIT license.
+// For all details and documentation:
+// https://github.com/dhruvaray/backbone-associations/
+//
+
+// Initial Setup
+// --------------
+(function () {
+ "use strict";
+
+ // Save a reference to the global object (`window` in the browser, `exports`
+ // on the server).
+ var root = this;
+
+ // The top-level namespace. All public Backbone classes and modules will be attached to this.
+ // Exported for the browser and CommonJS.
+ var _, Backbone, BackboneModel, BackboneCollection, ModelProto,
+ CollectionProto, defaultEvents, AssociatedModel, pathChecker,
+ collectionEvents, delimiters, pathSeparator;
+
+ if (typeof exports !== 'undefined') {
+ _ = require('underscore');
+ Backbone = require('backbone');
+ if (typeof module !== 'undefined' && module.exports) {
+ module.exports = Backbone;
+ }
+ exports = Backbone;
+ } else {
+ _ = root._;
+ Backbone = root.Backbone;
+ }
+ // Create local reference `Model` prototype.
+ BackboneModel = Backbone.Model;
+ BackboneCollection = Backbone.Collection;
+ ModelProto = BackboneModel.prototype;
+ CollectionProto = BackboneCollection.prototype;
+
+ // Built-in Backbone `events`.
+ defaultEvents = ["change", "add", "remove", "reset", "sort", "destroy"];
+ collectionEvents = ["reset", "sort"];
+
+ Backbone.Associations = {
+ VERSION: "0.5.4"
+ };
+
+ // Define `getter` and `setter` for `separator`
+ var getSeparator = function() {
+ return pathSeparator;
+ };
+ // Define `setSeperator`
+ var setSeparator = function(value) {
+ if (!_.isString(value) || _.size(value) < 1) {
+ value = ".";
+ }
+ // set private properties
+ pathSeparator = value;
+ pathChecker = new RegExp("[\\" + pathSeparator + "\\[\\]]+", "g");
+ delimiters = new RegExp("[^\\" + pathSeparator + "\\[\\]]+", "g");
+ };
+
+ try {
+ // Define `SEPERATOR` property to Backbone.Associations
+ Object.defineProperty(Backbone.Associations, 'SEPARATOR', {
+ enumerable: true,
+ get: getSeparator,
+ set: setSeparator
+ });
+ } catch (e) {}
+
+ // Backbone.AssociatedModel
+ // --------------
+
+ //Add `Many` and `One` relations to Backbone Object.
+ Backbone.Associations.Many = Backbone.Many = "Many";
+ Backbone.Associations.One = Backbone.One = "One";
+ Backbone.Associations.Self = Backbone.Self = "Self";
+ // Set default separator
+ Backbone.Associations.SEPARATOR = ".";
+ Backbone.Associations.getSeparator = getSeparator;
+ Backbone.Associations.setSeparator = setSeparator;
+ setSeparator();
+ // Define `AssociatedModel` (Extends Backbone.Model).
+ AssociatedModel = Backbone.AssociatedModel = Backbone.Associations.AssociatedModel = BackboneModel.extend({
+ // Define relations with Associated Model.
+ relations:undefined,
+ // Define `Model` property which can keep track of already fired `events`,
+ // and prevent redundant event to be triggered in case of cyclic model graphs.
+ _proxyCalls:undefined,
+
+ // Get the value of an attribute.
+ get:function (attr) {
+ var obj = ModelProto.get.call(this, attr);
+ return obj ? obj : this._getAttr.apply(this, arguments);
+ },
+
+ // Set a hash of model attributes on the Backbone Model.
+ set:function (key, value, options) {
+ var attributes, result;
+ // Duplicate backbone's behavior to allow separate key/value parameters,
+ // instead of a single 'attributes' object.
+ if (_.isObject(key) || key == null) {
+ attributes = key;
+ options = value;
+ } else {
+ attributes = {};
+ attributes[key] = value;
+ }
+ result = this._set(attributes, options);
+ // Trigger events which have been blocked until the entire object graph is updated.
+ this._processPendingEvents();
+ return result;
+
+ },
+
+ // Works with an attribute hash and options + fully qualified paths
+ _set:function (attributes, options) {
+ var attr, modelMap, modelId, obj, result = this;
+ if (!attributes) return this;
+ for (attr in attributes) {
+ //Create a map for each unique object whose attributes we want to set
+ modelMap || (modelMap = {});
+ if (attr.match(pathChecker)) {
+ var pathTokens = getPathArray(attr), initials = _.initial(pathTokens),
+ last = pathTokens[pathTokens.length - 1],
+ parentModel = this.get(initials);
+ if (parentModel instanceof AssociatedModel) {
+ obj = modelMap[parentModel.cid] || (modelMap[parentModel.cid] = {'model':parentModel, 'data':{}});
+ obj.data[last] = attributes[attr];
+ }
+ } else {
+ obj = modelMap[this.cid] || (modelMap[this.cid] = {'model':this, 'data':{}});
+ obj.data[attr] = attributes[attr];
+ }
+ }
+
+ if (modelMap) {
+ for (modelId in modelMap) {
+ obj = modelMap[modelId];
+ this._setAttr.call(obj.model, obj.data, options) || (result = false);
+
+ }
+ } else {
+ result = this._setAttr.call(this, attributes, options);
+ }
+ return result;
+
+ },
+
+ // Set a hash of model attributes on the object,
+ // fire Backbone `event` with options.
+ // It maintains relations between models during the set operation.
+ // It also bubbles up child events to the parent.
+ _setAttr:function (attributes, options) {
+ var attr;
+ // Extract attributes and options.
+ options || (options = {});
+ if (options.unset) for (attr in attributes) attributes[attr] = void 0;
+ this.parents = this.parents || [];
+
+ if (this.relations) {
+ // Iterate over `this.relations` and `set` model and collection values
+ // if `relations` are available.
+ _.each(this.relations, function (relation) {
+ var relationKey = relation.key,
+ relatedModel = relation.relatedModel,
+ collectionType = relation.collectionType,
+ map = relation.map,
+ currVal = this.attributes[relationKey],
+ idKey = currVal && currVal.idAttribute,
+ val, relationOptions, data, relationValue, newCtx = false;
+
+ // Call function if relatedModel is implemented as a function
+ if (relatedModel && !(relatedModel.prototype instanceof BackboneModel))
+ relatedModel = _.isFunction(relatedModel) ?
+ relatedModel.call(this, relation, attributes) :
+ relatedModel;
+
+ // Get class if relation and map is stored as a string.
+ if (relatedModel && _.isString(relatedModel)) {
+ relatedModel = (relatedModel === Backbone.Self) ? this.constructor : map2Scope(relatedModel);
+ }
+ collectionType && _.isString(collectionType) && (collectionType = map2Scope(collectionType));
+ map && _.isString(map) && (map = map2Scope(map));
+ // Merge in `options` specific to this relation.
+ relationOptions = relation.options ? _.extend({}, relation.options, options) : options;
+
+ if ((!relatedModel) && (!collectionType))
+ throw new Error('specify either a relatedModel or collectionType');
+
+ if (attributes[relationKey]) {
+ // Get value of attribute with relation key in `val`.
+ val = _.result(attributes, relationKey);
+ // Map `val` if a transformation function is provided.
+ val = map ? map.call(this, val, collectionType ? collectionType : relatedModel) : val;
+
+ // If `relation.type` is `Backbone.Many`,
+ // Create `Backbone.Collection` with passed data and perform Backbone `set`.
+ if (relation.type === Backbone.Many) {
+ // `collectionType` of defined `relation` should be instance of `Backbone.Collection`.
+ if (collectionType && !collectionType.prototype instanceof BackboneCollection) {
+ throw new Error('collectionType must inherit from Backbone.Collection');
+ }
+
+ if (currVal) {
+ // Setting this flag will prevent events from firing immediately. That way clients
+ // will not get events until the entire object graph is updated.
+ currVal._deferEvents = true;
+
+ // Use Backbone.Collection's `reset` or smart `set` method
+ currVal[relationOptions.reset ? 'reset' : 'set'](
+ val instanceof BackboneCollection ? val.models : val, relationOptions);
+
+ data = currVal;
+
+ } else {
+ newCtx = true;
+
+ if (val instanceof BackboneCollection) {
+ data = val;
+ } else {
+ data = collectionType ? new collectionType() : this._createCollection(relatedModel);
+ data[relationOptions.reset ? 'reset' : 'set'](val, relationOptions);
+ }
+ }
+
+ } else if (relation.type === Backbone.One) {
+
+ if (!relatedModel)
+ throw new Error('specify a relatedModel for Backbone.One type');
+
+ if (!(relatedModel.prototype instanceof Backbone.AssociatedModel))
+ throw new Error('specify an AssociatedModel for Backbone.One type');
+
+ data = val instanceof AssociatedModel ? val : new relatedModel(val, relationOptions);
+ //Is the passed in data for the same key?
+ if (currVal && data.attributes[idKey] &&
+ currVal.attributes[idKey] === data.attributes[idKey]) {
+ // Setting this flag will prevent events from firing immediately. That way clients
+ // will not get events until the entire object graph is updated.
+ currVal._deferEvents = true;
+ // Perform the traditional `set` operation
+ currVal._set(val instanceof AssociatedModel ? val.attributes : val, relationOptions);
+ data = currVal;
+ } else {
+ newCtx = true;
+ }
+
+ } else {
+ throw new Error('type attribute must be specified and have the values Backbone.One or Backbone.Many');
+ }
+
+
+ attributes[relationKey] = data;
+ relationValue = data;
+
+ // Add proxy events to respective parents.
+ // Only add callback if not defined or new Ctx has been identified.
+ if (newCtx || (relationValue && !relationValue._proxyCallback)) {
+ relationValue._proxyCallback = function () {
+ return this._bubbleEvent.call(this, relationKey, relationValue, arguments);
+ };
+ relationValue.on("all", relationValue._proxyCallback, this);
+ }
+
+ }
+ //Distinguish between the value of undefined versus a set no-op
+ if (attributes.hasOwnProperty(relationKey)) {
+ //Maintain reverse pointers - a.k.a parents
+ var updated = attributes[relationKey];
+ var original = this.attributes[relationKey];
+ if (updated) {
+ updated.parents = updated.parents || [];
+ (_.indexOf(updated.parents, this) == -1) && updated.parents.push(this);
+ } else if (original && original.parents.length > 0) { // New value is undefined
+ original.parents = _.difference(original.parents, [this]);
+ // Don't bubble to this parent anymore
+ original._proxyCallback && original.off("all", original._proxyCallback, this);
+ }
+ }
+ }, this);
+ }
+ // Return results for `BackboneModel.set`.
+ return ModelProto.set.call(this, attributes, options);
+ },
+ // Bubble-up event to `parent` Model
+ _bubbleEvent:function (relationKey, relationValue, eventArguments) {
+ var args = eventArguments,
+ opt = args[0].split(":"),
+ eventType = opt[0],
+ catch_all = args[0] == "nested-change",
+ eventObject = args[1],
+ colObject = args[2],
+ indexEventObject = -1,
+ _proxyCalls = relationValue._proxyCalls,
+ cargs,
+ eventPath,
+ basecolEventPath,
+ isDefaultEvent = _.indexOf(defaultEvents, eventType) !== -1;
+
+ //Short circuit the listen in to the nested-graph event
+ if (catch_all) return;
+
+ // Change the event name to a fully qualified path.
+ _.size(opt) > 1 && (eventPath = opt[1]);
+
+ if (_.indexOf(collectionEvents, eventType) !== -1) {
+ colObject = eventObject;
+ }
+
+ // Find the specific object in the collection which has changed.
+ if (relationValue instanceof BackboneCollection && isDefaultEvent && eventObject) {
+ var pathTokens = getPathArray(eventPath),
+ initialTokens = _.initial(pathTokens), colModel;
+
+ colModel = relationValue.find(function (model) {
+ if (eventObject === model) return true;
+ if (!model) return false;
+ var changedModel = model.get(initialTokens);
+
+ if ((changedModel instanceof AssociatedModel || changedModel instanceof BackboneCollection)
+ && eventObject === changedModel)
+ return true;
+
+ changedModel = model.get(pathTokens);
+
+ if ((changedModel instanceof AssociatedModel || changedModel instanceof BackboneCollection)
+ && eventObject === changedModel)
+ return true;
+
+ if (changedModel instanceof BackboneCollection && colObject
+ && colObject === changedModel)
+ return true;
+ });
+ colModel && (indexEventObject = relationValue.indexOf(colModel));
+ }
+
+ // Manipulate `eventPath`.
+ eventPath = relationKey + ((indexEventObject !== -1 && (eventType === "change" || eventPath)) ?
+ "[" + indexEventObject + "]" : "") + (eventPath ? pathSeparator + eventPath : "");
+
+ // Short circuit collection * events
+ if (/\[\*\]/g.test(eventPath)) return this;
+ basecolEventPath = eventPath.replace(/\[\d+\]/g, '[*]');
+
+ cargs = [];
+ cargs.push.apply(cargs, args);
+ cargs[0] = eventType + ":" + eventPath;
+
+ // If event has been already triggered as result of same source `eventPath`,
+ // no need to re-trigger event to prevent cycle.
+ _proxyCalls = relationValue._proxyCalls = (_proxyCalls || {});
+ if (this._isEventAvailable.call(this, _proxyCalls, eventPath)) return this;
+
+ // Add `eventPath` in `_proxyCalls` to keep track of already triggered `event`.
+ _proxyCalls[eventPath] = true;
+
+ // Set up previous attributes correctly.
+ if ("change" === eventType) {
+ this._previousAttributes[relationKey] = relationValue._previousAttributes;
+ this.changed[relationKey] = relationValue;
+ }
+
+ // Bubble up event to parent `model` with new changed arguments.
+ this.trigger.apply(this, cargs);
+
+ //Only fire for change. Not change:attribute
+ if ("change" === eventType && this.get(eventPath) != args[2]) {
+ var ncargs = ["nested-change", eventPath, args[1]];
+ args[2] && ncargs.push(args[2]); //args[2] will be options if present
+ this.trigger.apply(this, ncargs);
+ }
+
+ // Remove `eventPath` from `_proxyCalls`,
+ // if `eventPath` and `_proxyCalls` are available,
+ // which allow event to be triggered on for next operation of `set`.
+ if (_proxyCalls && eventPath) delete _proxyCalls[eventPath];
+
+ // Create a collection modified event with wild-card
+ if (eventPath !== basecolEventPath) {
+ cargs[0] = eventType + ":" + basecolEventPath;
+ this.trigger.apply(this, cargs);
+ }
+
+ return this;
+ },
+
+ // Has event been fired from this source. Used to prevent event recursion in cyclic graphs
+ _isEventAvailable:function (_proxyCalls, path) {
+ return _.find(_proxyCalls, function (value, eventKey) {
+ return path.indexOf(eventKey, path.length - eventKey.length) !== -1;
+ });
+ },
+
+ // Returns New `collection` of type `relation.relatedModel`.
+ _createCollection:function (type) {
+ var collection, relatedModel = type;
+ _.isString(relatedModel) && (relatedModel = map2Scope(relatedModel));
+ // Creates new `Backbone.Collection` and defines model class.
+ if (relatedModel && (relatedModel.prototype instanceof AssociatedModel) || _.isFunction(relatedModel)) {
+ collection = new BackboneCollection();
+ collection.model = relatedModel;
+ } else {
+ throw new Error('type must inherit from Backbone.AssociatedModel');
+ }
+ return collection;
+ },
+
+ // Process all pending events after the entire object graph has been updated
+ _processPendingEvents:function () {
+ if (!this._processedEvents) {
+ this._processedEvents = true;
+
+ this._deferEvents = false;
+
+ // Trigger all pending events
+ _.each(this._pendingEvents, function (e) {
+ e.c.trigger.apply(e.c, e.a);
+ });
+
+ this._pendingEvents = [];
+
+ // Traverse down the object graph and call process pending events on sub-trees
+ _.each(this.relations, function (relation) {
+ var val = this.attributes[relation.key];
+ val && val._processPendingEvents();
+ }, this);
+
+ delete this._processedEvents;
+ }
+ },
+
+ // Override trigger to defer events in the object graph.
+ trigger:function (name) {
+ // Defer event processing
+ if (this._deferEvents) {
+ this._pendingEvents = this._pendingEvents || [];
+ // Maintain a queue of pending events to trigger after the entire object graph is updated.
+ this._pendingEvents.push({c:this, a:arguments});
+ } else {
+ ModelProto.trigger.apply(this, arguments);
+ }
+ },
+
+ // The JSON representation of the model.
+ toJSON:function (options) {
+ var json = {}, aJson;
+ json[this.idAttribute] = this.id;
+ if (!this.visited) {
+ this.visited = true;
+ // Get json representation from `BackboneModel.toJSON`.
+ json = ModelProto.toJSON.apply(this, arguments);
+ // If `this.relations` is defined, iterate through each `relation`
+ // and added it's json representation to parents' json representation.
+ if (this.relations) {
+ _.each(this.relations, function (relation) {
+ var attr = this.attributes[relation.key];
+ if (attr) {
+ aJson = attr.toJSON ? attr.toJSON(options) : attr;
+ json[relation.key] = _.isArray(aJson) ? _.compact(aJson) : aJson;
+ }
+ }, this);
+ }
+ delete this.visited;
+ }
+ return json;
+ },
+
+ // Create a new model with identical attributes to this one.
+ clone:function () {
+ return new this.constructor(this.toJSON());
+ },
+
+ // Call this if you want to set an `AssociatedModel` to a falsy value like undefined/null directly.
+ // Not calling this will leak memory and have wrong parents.
+ // See test case "parent relations"
+ cleanup:function () {
+ _.each(this.relations, function (relation) {
+ var val = this.attributes[relation.key];
+ val && (val.parents = _.difference(val.parents, [this]));
+ }, this);
+ this.off();
+ },
+
+ // Navigate the path to the leaf object in the path to query for the attribute value
+ _getAttr:function (path) {
+
+ var result = this,
+ //Tokenize the path
+ attrs = getPathArray(path),
+ key,
+ i;
+ if (_.size(attrs) < 1) return;
+ for (i = 0; i < attrs.length; i++) {
+ key = attrs[i];
+ if (!result) break;
+ //Navigate the path to get to the result
+ result = result instanceof BackboneCollection
+ ? (isNaN(key) ? undefined : result.at(key))
+ : result.attributes[key];
+ }
+ return result;
+ }
+ });
+
+ // Tokenize the fully qualified event path
+ var getPathArray = function (path) {
+ if (path === '') return [''];
+ return _.isString(path) ? (path.match(delimiters)) : path || [];
+ };
+
+ var map2Scope = function (path) {
+ return _.reduce(path.split(pathSeparator), function (memo, elem) {
+ return memo[elem];
+ }, root);
+ };
+
+ //Infer the relation from the collection's parents and find the appropriate map for the passed in `models`
+ var map2models = function (parents, target, models) {
+ var relation, surrogate;
+ //Iterate over collection's parents
+ _.find(parents, function (parent) {
+ //Iterate over relations
+ relation = _.find(parent.relations, function (rel) {
+ return parent.get(rel.key) === target;
+ }, this);
+ if (relation) {
+ surrogate = parent;//surrogate for transformation
+ return true;//break;
+ }
+ }, this);
+
+ //If we found a relation and it has a mapping function
+ if (relation && relation.map) {
+ return relation.map.call(surrogate, models, target);
+ }
+ return models;
+ };
+
+ var proxies = {};
+ // Proxy Backbone collection methods
+ _.each(['set', 'remove', 'reset'], function (method) {
+ proxies[method] = BackboneCollection.prototype[method];
+
+ CollectionProto[method] = function (models, options) {
+ //Short-circuit if this collection doesn't hold `AssociatedModels`
+ if (this.model.prototype instanceof AssociatedModel && this.parents) {
+ //Find a map function if available and perform a transformation
+ arguments[0] = map2models(this.parents, this, models);
+ }
+ return proxies[method].apply(this, arguments);
+ }
+ });
+
+ // Override trigger to defer events in the object graph.
+ proxies['trigger'] = CollectionProto['trigger'];
+ CollectionProto['trigger'] = function (name) {
+ if (this._deferEvents) {
+ this._pendingEvents = this._pendingEvents || [];
+ // Maintain a queue of pending events to trigger after the entire object graph is updated.
+ this._pendingEvents.push({c:this, a:arguments});
+ } else {
+ proxies['trigger'].apply(this, arguments);
+ }
+ };
+
+ // Attach process pending event functionality on collections as well. Re-use from `AssociatedModel`
+ CollectionProto._processPendingEvents = AssociatedModel.prototype._processPendingEvents;
+
+
+}).call(this);
\ No newline at end of file
diff --git a/static/js/lib/backbone.marionette.js b/static/js/lib/backbone.marionette.js
index 9a179f7..71f44e6 100644
--- a/static/js/lib/backbone.marionette.js
+++ b/static/js/lib/backbone.marionette.js
@@ -2001,7 +2001,7 @@ Marionette.AppRouter = Backbone.Router.extend({
constructor: function(options){
Backbone.Router.prototype.constructor.apply(this, slice(arguments));
-
+
this.options = options || {};
var appRoutes = Marionette.getOption(this, "appRoutes");
diff --git a/static/js/lib/backbone.relational.js b/static/js/lib/backbone.relational.js
deleted file mode 100644
index c79a81e..0000000
--- a/static/js/lib/backbone.relational.js
+++ /dev/null
@@ -1,1943 +0,0 @@
-/* vim: set tabstop=4 softtabstop=4 shiftwidth=4 noexpandtab: */
-/**
- * Backbone-relational.js 0.8.6
- * (c) 2011-2013 Paul Uithol and contributors (https://github.com/PaulUithol/Backbone-relational/graphs/contributors)
- *
- * Backbone-relational may be freely distributed under the MIT license; see the accompanying LICENSE.txt.
- * For details and documentation: https://github.com/PaulUithol/Backbone-relational.
- * Depends on Backbone (and thus on Underscore as well): https://github.com/documentcloud/backbone.
- */
-( function( undefined ) {
- "use strict";
-
- /**
- * CommonJS shim
- **/
- var _, Backbone, exports;
- if ( typeof window === 'undefined' ) {
- _ = require( 'underscore' );
- Backbone = require( 'backbone' );
- exports = Backbone;
- typeof module === 'undefined' || ( module.exports = exports );
- }
- else {
- _ = window._;
- Backbone = window.Backbone;
- exports = window;
- }
-
- Backbone.Relational = {
- showWarnings: true
- };
-
- /**
- * Semaphore mixin; can be used as both binary and counting.
- **/
- Backbone.Semaphore = {
- _permitsAvailable: null,
- _permitsUsed: 0,
-
- acquire: function() {
- if ( this._permitsAvailable && this._permitsUsed >= this._permitsAvailable ) {
- throw new Error( 'Max permits acquired' );
- }
- else {
- this._permitsUsed++;
- }
- },
-
- release: function() {
- if ( this._permitsUsed === 0 ) {
- throw new Error( 'All permits released' );
- }
- else {
- this._permitsUsed--;
- }
- },
-
- isLocked: function() {
- return this._permitsUsed > 0;
- },
-
- setAvailablePermits: function( amount ) {
- if ( this._permitsUsed > amount ) {
- throw new Error( 'Available permits cannot be less than used permits' );
- }
- this._permitsAvailable = amount;
- }
- };
-
- /**
- * A BlockingQueue that accumulates items while blocked (via 'block'),
- * and processes them when unblocked (via 'unblock').
- * Process can also be called manually (via 'process').
- */
- Backbone.BlockingQueue = function() {
- this._queue = [];
- };
- _.extend( Backbone.BlockingQueue.prototype, Backbone.Semaphore, {
- _queue: null,
-
- add: function( func ) {
- if ( this.isBlocked() ) {
- this._queue.push( func );
- }
- else {
- func();
- }
- },
-
- // Some of the queued events may trigger other blocking events. By
- // copying the queue here it allows queued events to process closer to
- // the natural order.
- //
- // queue events [ 'A', 'B', 'C' ]
- // A handler of 'B' triggers 'D' and 'E'
- // By copying `this._queue` this executes:
- // [ 'A', 'B', 'D', 'E', 'C' ]
- // The same order the would have executed if they didn't have to be
- // delayed and queued.
- process: function() {
- var queue = this._queue;
- this._queue = [];
- while ( queue && queue.length ) {
- queue.shift()();
- }
- },
-
- block: function() {
- this.acquire();
- },
-
- unblock: function() {
- this.release();
- if ( !this.isBlocked() ) {
- this.process();
- }
- },
-
- isBlocked: function() {
- return this.isLocked();
- }
- });
- /**
- * Global event queue. Accumulates external events ('add:
', 'remove:' and 'change:')
- * until the top-level object is fully initialized (see 'Backbone.RelationalModel').
- */
- Backbone.Relational.eventQueue = new Backbone.BlockingQueue();
-
- /**
- * Backbone.Store keeps track of all created (and destruction of) Backbone.RelationalModel.
- * Handles lookup for relations.
- */
- Backbone.Store = function() {
- this._collections = [];
- this._reverseRelations = [];
- this._orphanRelations = [];
- this._subModels = [];
- this._modelScopes = [ exports ];
- };
- _.extend( Backbone.Store.prototype, Backbone.Events, {
- /**
- * Create a new `Relation`.
- * @param {Backbone.RelationalModel} [model]
- * @param {Object} relation
- * @param {Object} [options]
- */
- initializeRelation: function( model, relation, options ) {
- var type = !_.isString( relation.type ) ? relation.type : Backbone[ relation.type ] || this.getObjectByName( relation.type );
- if ( type && type.prototype instanceof Backbone.Relation ) {
- new type( model, relation, options ); // Also pushes the new Relation into `model._relations`
- }
- else {
- Backbone.Relational.showWarnings && typeof console !== 'undefined' && console.warn( 'Relation=%o; missing or invalid relation type!', relation );
- }
- },
-
- /**
- * Add a scope for `getObjectByName` to look for model types by name.
- * @param {Object} scope
- */
- addModelScope: function( scope ) {
- this._modelScopes.push( scope );
- },
-
- /**
- * Remove a scope.
- * @param {Object} scope
- */
- removeModelScope: function( scope ) {
- this._modelScopes = _.without( this._modelScopes, scope );
- },
-
- /**
- * Add a set of subModelTypes to the store, that can be used to resolve the '_superModel'
- * for a model later in 'setupSuperModel'.
- *
- * @param {Backbone.RelationalModel} subModelTypes
- * @param {Backbone.RelationalModel} superModelType
- */
- addSubModels: function( subModelTypes, superModelType ) {
- this._subModels.push({
- 'superModelType': superModelType,
- 'subModels': subModelTypes
- });
- },
-
- /**
- * Check if the given modelType is registered as another model's subModel. If so, add it to the super model's
- * '_subModels', and set the modelType's '_superModel', '_subModelTypeName', and '_subModelTypeAttribute'.
- *
- * @param {Backbone.RelationalModel} modelType
- */
- setupSuperModel: function( modelType ) {
- _.find( this._subModels, function( subModelDef ) {
- return _.find( subModelDef.subModels || [], function( subModelTypeName, typeValue ) {
- var subModelType = this.getObjectByName( subModelTypeName );
-
- if ( modelType === subModelType ) {
- // Set 'modelType' as a child of the found superModel
- subModelDef.superModelType._subModels[ typeValue ] = modelType;
-
- // Set '_superModel', '_subModelTypeValue', and '_subModelTypeAttribute' on 'modelType'.
- modelType._superModel = subModelDef.superModelType;
- modelType._subModelTypeValue = typeValue;
- modelType._subModelTypeAttribute = subModelDef.superModelType.prototype.subModelTypeAttribute;
- return true;
- }
- }, this );
- }, this );
- },
-
- /**
- * Add a reverse relation. Is added to the 'relations' property on model's prototype, and to
- * existing instances of 'model' in the store as well.
- * @param {Object} relation
- * @param {Backbone.RelationalModel} relation.model
- * @param {String} relation.type
- * @param {String} relation.key
- * @param {String|Object} relation.relatedModel
- */
- addReverseRelation: function( relation ) {
- var exists = _.any( this._reverseRelations, function( rel ) {
- return _.all( relation || [], function( val, key ) {
- return val === rel[ key ];
- });
- });
-
- if ( !exists && relation.model && relation.type ) {
- this._reverseRelations.push( relation );
- this._addRelation( relation.model, relation );
- this.retroFitRelation( relation );
- }
- },
-
- /**
- * Deposit a `relation` for which the `relatedModel` can't be resolved at the moment.
- *
- * @param {Object} relation
- */
- addOrphanRelation: function( relation ) {
- var exists = _.any( this._orphanRelations, function( rel ) {
- return _.all( relation || [], function( val, key ) {
- return val === rel[ key ];
- });
- });
-
- if ( !exists && relation.model && relation.type ) {
- this._orphanRelations.push( relation );
- }
- },
-
- /**
- * Try to initialize any `_orphanRelation`s
- */
- processOrphanRelations: function() {
- // Make sure to operate on a copy since we're removing while iterating
- _.each( this._orphanRelations.slice( 0 ), function( rel ) {
- var relatedModel = Backbone.Relational.store.getObjectByName( rel.relatedModel );
- if ( relatedModel ) {
- this.initializeRelation( null, rel );
- this._orphanRelations = _.without( this._orphanRelations, rel );
- }
- }, this );
- },
-
- /**
- *
- * @param {Backbone.RelationalModel.constructor} type
- * @param {Object} relation
- * @private
- */
- _addRelation: function( type, relation ) {
- if ( !type.prototype.relations ) {
- type.prototype.relations = [];
- }
- type.prototype.relations.push( relation );
-
- _.each( type._subModels || [], function( subModel ) {
- this._addRelation( subModel, relation );
- }, this );
- },
-
- /**
- * Add a 'relation' to all existing instances of 'relation.model' in the store
- * @param {Object} relation
- */
- retroFitRelation: function( relation ) {
- var coll = this.getCollection( relation.model, false );
- coll && coll.each( function( model ) {
- if ( !( model instanceof relation.model ) ) {
- return;
- }
-
- new relation.type( model, relation );
- }, this );
- },
-
- /**
- * Find the Store's collection for a certain type of model.
- * @param {Backbone.RelationalModel} type
- * @param {Boolean} [create=true] Should a collection be created if none is found?
- * @return {Backbone.Collection} A collection if found (or applicable for 'model'), or null
- */
- getCollection: function( type, create ) {
- if ( type instanceof Backbone.RelationalModel ) {
- type = type.constructor;
- }
-
- var rootModel = type;
- while ( rootModel._superModel ) {
- rootModel = rootModel._superModel;
- }
-
- var coll = _.find( this._collections, function(item) {
- return item.model === rootModel;
- });
-
- if ( !coll && create !== false ) {
- coll = this._createCollection( rootModel );
- }
-
- return coll;
- },
-
- /**
- * Find a model type on one of the modelScopes by name. Names are split on dots.
- * @param {String} name
- * @return {Object}
- */
- getObjectByName: function( name ) {
- var parts = name.split( '.' ),
- type = null;
-
- _.find( this._modelScopes, function( scope ) {
- type = _.reduce( parts || [], function( memo, val ) {
- return memo ? memo[ val ] : undefined;
- }, scope );
-
- if ( type && type !== scope ) {
- return true;
- }
- }, this );
-
- return type;
- },
-
- _createCollection: function( type ) {
- var coll;
-
- // If 'type' is an instance, take its constructor
- if ( type instanceof Backbone.RelationalModel ) {
- type = type.constructor;
- }
-
- // Type should inherit from Backbone.RelationalModel.
- if ( type.prototype instanceof Backbone.RelationalModel ) {
- coll = new Backbone.Collection();
- coll.model = type;
-
- this._collections.push( coll );
- }
-
- return coll;
- },
-
- /**
- * Find the attribute that is to be used as the `id` on a given object
- * @param type
- * @param {String|Number|Object|Backbone.RelationalModel} item
- * @return {String|Number}
- */
- resolveIdForItem: function( type, item ) {
- var id = _.isString( item ) || _.isNumber( item ) ? item : null;
-
- if ( id === null ) {
- if ( item instanceof Backbone.RelationalModel ) {
- id = item.id;
- }
- else if ( _.isObject( item ) ) {
- id = item[ type.prototype.idAttribute ];
- }
- }
-
- // Make all falsy values `null` (except for 0, which could be an id.. see '/issues/179')
- if ( !id && id !== 0 ) {
- id = null;
- }
-
- return id;
- },
-
- /**
- * Find a specific model of a certain `type` in the store
- * @param type
- * @param {String|Number|Object|Backbone.RelationalModel} item
- */
- find: function( type, item ) {
- var id = this.resolveIdForItem( type, item );
- var coll = this.getCollection( type );
-
- // Because the found object could be of any of the type's superModel
- // types, only return it if it's actually of the type asked for.
- if ( coll ) {
- var obj = coll.get( id );
-
- if ( obj instanceof type ) {
- return obj;
- }
- }
-
- return null;
- },
-
- /**
- * Add a 'model' to its appropriate collection. Retain the original contents of 'model.collection'.
- * @param {Backbone.RelationalModel} model
- */
- register: function( model ) {
- var coll = this.getCollection( model );
-
- if ( coll ) {
- var modelColl = model.collection;
- coll.add( model );
- this.listenTo( model, 'destroy', this.unregister, this );
- this.listenTo( model, 'relational:unregister', this.unregister, this );
- model.collection = modelColl;
- }
- },
-
- /**
- * Check if the given model may use the given `id`
- * @param model
- * @param [id]
- */
- checkId: function( model, id ) {
- var coll = this.getCollection( model ),
- duplicate = coll && coll.get( id );
-
- if ( duplicate && model !== duplicate ) {
- if ( Backbone.Relational.showWarnings && typeof console !== 'undefined' ) {
- console.warn( 'Duplicate id! Old RelationalModel=%o, new RelationalModel=%o', duplicate, model );
- }
-
- throw new Error( "Cannot instantiate more than one Backbone.RelationalModel with the same id per type!" );
- }
- },
-
- /**
- * Explicitly update a model's id in its store collection
- * @param {Backbone.RelationalModel} model
- */
- update: function( model ) {
- var coll = this.getCollection( model );
- // This triggers updating the lookup indices kept in a collection
- coll._onModelEvent( 'change:' + model.idAttribute, model, coll );
-
- // Trigger an event on model so related models (having the model's new id in their keyContents) can add it.
- model.trigger( 'relational:change:id', model, coll );
- },
-
- /**
- * Remove a 'model' from the store.
- * @param {Backbone.RelationalModel} model
- * @param {Backbone.Collection} [collection]
- * @param {Object} [options]
- */
- unregister: function( model, collection, options ) {
- this.stopListening( model );
- var coll = this.getCollection( model );
- coll && coll.remove( model, options );
- },
-
- /**
- * Reset the `store` to it's original state. The `reverseRelations` are kept though, since attempting to
- * re-initialize these on models would lead to a large amount of warnings.
- */
- reset: function() {
- this.stopListening();
- this._collections = [];
- this._subModels = [];
- this._modelScopes = [ exports ];
- }
- });
- Backbone.Relational.store = new Backbone.Store();
-
- /**
- * The main Relation class, from which 'HasOne' and 'HasMany' inherit. Internally, 'relational:' events
- * are used to regulate addition and removal of models from relations.
- *
- * @param {Backbone.RelationalModel} [instance] Model that this relation is created for. If no model is supplied,
- * Relation just tries to instantiate it's `reverseRelation` if specified, and bails out after that.
- * @param {Object} options
- * @param {string} options.key
- * @param {Backbone.RelationalModel.constructor} options.relatedModel
- * @param {Boolean|String} [options.includeInJSON=true] Serialize the given attribute for related model(s)' in toJSON, or just their ids.
- * @param {Boolean} [options.createModels=true] Create objects from the contents of keys if the object is not found in Backbone.store.
- * @param {Object} [options.reverseRelation] Specify a bi-directional relation. If provided, Relation will reciprocate
- * the relation to the 'relatedModel'. Required and optional properties match 'options', except that it also needs
- * {Backbone.Relation|String} type ('HasOne' or 'HasMany').
- * @param {Object} opts
- */
- Backbone.Relation = function( instance, options, opts ) {
- this.instance = instance;
- // Make sure 'options' is sane, and fill with defaults from subclasses and this object's prototype
- options = _.isObject( options ) ? options : {};
- this.reverseRelation = _.defaults( options.reverseRelation || {}, this.options.reverseRelation );
- this.options = _.defaults( options, this.options, Backbone.Relation.prototype.options );
-
- this.reverseRelation.type = !_.isString( this.reverseRelation.type ) ? this.reverseRelation.type :
- Backbone[ this.reverseRelation.type ] || Backbone.Relational.store.getObjectByName( this.reverseRelation.type );
-
- this.key = this.options.key;
- this.keySource = this.options.keySource || this.key;
- this.keyDestination = this.options.keyDestination || this.keySource || this.key;
-
- this.model = this.options.model || this.instance.constructor;
-
- this.relatedModel = this.options.relatedModel;
-
- if ( _.isFunction( this.relatedModel ) && !( this.relatedModel.prototype instanceof Backbone.RelationalModel ) ) {
- this.relatedModel = _.result( this, 'relatedModel' );
- }
- if ( _.isString( this.relatedModel ) ) {
- this.relatedModel = Backbone.Relational.store.getObjectByName( this.relatedModel );
- }
-
- if ( !this.checkPreconditions() ) {
- return;
- }
-
- // Add the reverse relation on 'relatedModel' to the store's reverseRelations
- if ( !this.options.isAutoRelation && this.reverseRelation.type && this.reverseRelation.key ) {
- Backbone.Relational.store.addReverseRelation( _.defaults( {
- isAutoRelation: true,
- model: this.relatedModel,
- relatedModel: this.model,
- reverseRelation: this.options // current relation is the 'reverseRelation' for its own reverseRelation
- },
- this.reverseRelation // Take further properties from this.reverseRelation (type, key, etc.)
- ) );
- }
-
- if ( instance ) {
- var contentKey = this.keySource;
- if ( contentKey !== this.key && typeof this.instance.get( this.key ) === 'object' ) {
- contentKey = this.key;
- }
-
- this.setKeyContents( this.instance.get( contentKey ) );
- this.relatedCollection = Backbone.Relational.store.getCollection( this.relatedModel );
-
- // Explicitly clear 'keySource', to prevent a leaky abstraction if 'keySource' differs from 'key'.
- if ( this.keySource !== this.key ) {
- delete this.instance.attributes[ this.keySource ];
- }
-
- // Add this Relation to instance._relations
- this.instance._relations[ this.key ] = this;
-
- this.initialize( opts );
-
- if ( this.options.autoFetch ) {
- this.instance.fetchRelated( this.key, _.isObject( this.options.autoFetch ) ? this.options.autoFetch : {} );
- }
-
- // When 'relatedModel' are created or destroyed, check if it affects this relation.
- this.listenTo( this.instance, 'destroy', this.destroy )
- .listenTo( this.relatedCollection, 'relational:add relational:change:id', this.tryAddRelated )
- .listenTo( this.relatedCollection, 'relational:remove', this.removeRelated )
- }
- };
- // Fix inheritance :\
- Backbone.Relation.extend = Backbone.Model.extend;
- // Set up all inheritable **Backbone.Relation** properties and methods.
- _.extend( Backbone.Relation.prototype, Backbone.Events, Backbone.Semaphore, {
- options: {
- createModels: true,
- includeInJSON: true,
- isAutoRelation: false,
- autoFetch: false,
- parse: false
- },
-
- instance: null,
- key: null,
- keyContents: null,
- relatedModel: null,
- relatedCollection: null,
- reverseRelation: null,
- related: null,
-
- /**
- * Check several pre-conditions.
- * @return {Boolean} True if pre-conditions are satisfied, false if they're not.
- */
- checkPreconditions: function() {
- var i = this.instance,
- k = this.key,
- m = this.model,
- rm = this.relatedModel,
- warn = Backbone.Relational.showWarnings && typeof console !== 'undefined';
-
- if ( !m || !k || !rm ) {
- warn && console.warn( 'Relation=%o: missing model, key or relatedModel (%o, %o, %o).', this, m, k, rm );
- return false;
- }
- // Check if the type in 'model' inherits from Backbone.RelationalModel
- if ( !( m.prototype instanceof Backbone.RelationalModel ) ) {
- warn && console.warn( 'Relation=%o: model does not inherit from Backbone.RelationalModel (%o).', this, i );
- return false;
- }
- // Check if the type in 'relatedModel' inherits from Backbone.RelationalModel
- if ( !( rm.prototype instanceof Backbone.RelationalModel ) ) {
- warn && console.warn( 'Relation=%o: relatedModel does not inherit from Backbone.RelationalModel (%o).', this, rm );
- return false;
- }
- // Check if this is not a HasMany, and the reverse relation is HasMany as well
- if ( this instanceof Backbone.HasMany && this.reverseRelation.type === Backbone.HasMany ) {
- warn && console.warn( 'Relation=%o: relation is a HasMany, and the reverseRelation is HasMany as well.', this );
- return false;
- }
- // Check if we're not attempting to create a relationship on a `key` that's already used.
- if ( i && _.keys( i._relations ).length ) {
- var existing = _.find( i._relations, function( rel ) {
- return rel.key === k;
- }, this );
-
- if ( existing ) {
- warn && console.warn( 'Cannot create relation=%o on %o for model=%o: already taken by relation=%o.',
- this, k, i, existing );
- return false;
- }
- }
-
- return true;
- },
-
- /**
- * Set the related model(s) for this relation
- * @param {Backbone.Model|Backbone.Collection} related
- */
- setRelated: function( related ) {
- this.related = related;
-
- this.instance.acquire();
- this.instance.attributes[ this.key ] = related;
- this.instance.release();
- },
-
- /**
- * Determine if a relation (on a different RelationalModel) is the reverse
- * relation of the current one.
- * @param {Backbone.Relation} relation
- * @return {Boolean}
- */
- _isReverseRelation: function( relation ) {
- return relation.instance instanceof this.relatedModel && this.reverseRelation.key === relation.key &&
- this.key === relation.reverseRelation.key;
- },
-
- /**
- * Get the reverse relations (pointing back to 'this.key' on 'this.instance') for the currently related model(s).
- * @param {Backbone.RelationalModel} [model] Get the reverse relations for a specific model.
- * If not specified, 'this.related' is used.
- * @return {Backbone.Relation[]}
- */
- getReverseRelations: function( model ) {
- var reverseRelations = [];
- // Iterate over 'model', 'this.related.models' (if this.related is a Backbone.Collection), or wrap 'this.related' in an array.
- var models = !_.isUndefined( model ) ? [ model ] : this.related && ( this.related.models || [ this.related ] );
- _.each( models || [], function( related ) {
- _.each( related.getRelations() || [], function( relation ) {
- if ( this._isReverseRelation( relation ) ) {
- reverseRelations.push( relation );
- }
- }, this );
- }, this );
-
- return reverseRelations;
- },
-
- /**
- * When `this.instance` is destroyed, cleanup our relations.
- * Get reverse relation, call removeRelated on each.
- */
- destroy: function() {
- this.stopListening();
-
- if ( this instanceof Backbone.HasOne ) {
- this.setRelated( null );
- }
- else if ( this instanceof Backbone.HasMany ) {
- this.setRelated( this._prepareCollection() );
- }
-
- _.each( this.getReverseRelations(), function( relation ) {
- relation.removeRelated( this.instance );
- }, this );
- }
- });
-
- Backbone.HasOne = Backbone.Relation.extend({
- options: {
- reverseRelation: { type: 'HasMany' }
- },
-
- initialize: function( opts ) {
- this.listenTo( this.instance, 'relational:change:' + this.key, this.onChange );
-
- var related = this.findRelated( opts );
- this.setRelated( related );
-
- // Notify new 'related' object of the new relation.
- _.each( this.getReverseRelations(), function( relation ) {
- relation.addRelated( this.instance, opts );
- }, this );
- },
-
- /**
- * Find related Models.
- * @param {Object} [options]
- * @return {Backbone.Model}
- */
- findRelated: function( options ) {
- var related = null;
-
- options = _.defaults( { parse: this.options.parse }, options );
-
- if ( this.keyContents instanceof this.relatedModel ) {
- related = this.keyContents;
- }
- else if ( this.keyContents || this.keyContents === 0 ) { // since 0 can be a valid `id` as well
- var opts = _.defaults( { create: this.options.createModels }, options );
- related = this.relatedModel.findOrCreate( this.keyContents, opts );
- }
-
- // Nullify `keyId` if we have a related model; in case it was already part of the relation
- if ( related ) {
- this.keyId = null;
- }
-
- return related;
- },
-
- /**
- * Normalize and reduce `keyContents` to an `id`, for easier comparison
- * @param {String|Number|Backbone.Model} keyContents
- */
- setKeyContents: function( keyContents ) {
- this.keyContents = keyContents;
- this.keyId = Backbone.Relational.store.resolveIdForItem( this.relatedModel, this.keyContents );
- },
-
- /**
- * Event handler for `change:`.
- * If the key is changed, notify old & new reverse relations and initialize the new relation.
- */
- onChange: function( model, attr, options ) {
- // Don't accept recursive calls to onChange (like onChange->findRelated->findOrCreate->initializeRelations->addRelated->onChange)
- if ( this.isLocked() ) {
- return;
- }
- this.acquire();
- options = options ? _.clone( options ) : {};
-
- // 'options.__related' is set by 'addRelated'/'removeRelated'. If it is set, the change
- // is the result of a call from a relation. If it's not, the change is the result of
- // a 'set' call on this.instance.
- var changed = _.isUndefined( options.__related ),
- oldRelated = changed ? this.related : options.__related;
-
- if ( changed ) {
- this.setKeyContents( attr );
- var related = this.findRelated( options );
- this.setRelated( related );
- }
-
- // Notify old 'related' object of the terminated relation
- if ( oldRelated && this.related !== oldRelated ) {
- _.each( this.getReverseRelations( oldRelated ), function( relation ) {
- relation.removeRelated( this.instance, null, options );
- }, this );
- }
-
- // Notify new 'related' object of the new relation. Note we do re-apply even if this.related is oldRelated;
- // that can be necessary for bi-directional relations if 'this.instance' was created after 'this.related'.
- // In that case, 'this.instance' will already know 'this.related', but the reverse might not exist yet.
- _.each( this.getReverseRelations(), function( relation ) {
- relation.addRelated( this.instance, options );
- }, this );
-
- // Fire the 'change:' event if 'related' was updated
- if ( !options.silent && this.related !== oldRelated ) {
- var dit = this;
- this.changed = true;
- Backbone.Relational.eventQueue.add( function() {
- dit.instance.trigger( 'change:' + dit.key, dit.instance, dit.related, options, true );
- dit.changed = false;
- });
- }
- this.release();
- },
-
- /**
- * If a new 'this.relatedModel' appears in the 'store', try to match it to the last set 'keyContents'
- */
- tryAddRelated: function( model, coll, options ) {
- if ( ( this.keyId || this.keyId === 0 ) && model.id === this.keyId ) { // since 0 can be a valid `id` as well
- this.addRelated( model, options );
- this.keyId = null;
- }
- },
-
- addRelated: function( model, options ) {
- // Allow 'model' to set up its relations before proceeding.
- // (which can result in a call to 'addRelated' from a relation of 'model')
- var dit = this;
- model.queue( function() {
- if ( model !== dit.related ) {
- var oldRelated = dit.related || null;
- dit.setRelated( model );
- dit.onChange( dit.instance, model, _.defaults( { __related: oldRelated }, options ) );
- }
- });
- },
-
- removeRelated: function( model, coll, options ) {
- if ( !this.related ) {
- return;
- }
-
- if ( model === this.related ) {
- var oldRelated = this.related || null;
- this.setRelated( null );
- this.onChange( this.instance, model, _.defaults( { __related: oldRelated }, options ) );
- }
- }
- });
-
- Backbone.HasMany = Backbone.Relation.extend({
- collectionType: null,
-
- options: {
- reverseRelation: { type: 'HasOne' },
- collectionType: Backbone.Collection,
- collectionKey: true,
- collectionOptions: {}
- },
-
- initialize: function( opts ) {
- this.listenTo( this.instance, 'relational:change:' + this.key, this.onChange );
-
- // Handle a custom 'collectionType'
- this.collectionType = this.options.collectionType;
- if ( _.isFunction( this.collectionType ) && this.collectionType !== Backbone.Collection && !( this.collectionType.prototype instanceof Backbone.Collection ) ) {
- this.collectionType = _.result( this, 'collectionType' );
- }
- if ( _.isString( this.collectionType ) ) {
- this.collectionType = Backbone.Relational.store.getObjectByName( this.collectionType );
- }
- if ( this.collectionType !== Backbone.Collection && !( this.collectionType.prototype instanceof Backbone.Collection ) ) {
- throw new Error( '`collectionType` must inherit from Backbone.Collection' );
- }
-
- var related = this.findRelated( opts );
- this.setRelated( related );
- },
-
- /**
- * Bind events and setup collectionKeys for a collection that is to be used as the backing store for a HasMany.
- * If no 'collection' is supplied, a new collection will be created of the specified 'collectionType' option.
- * @param {Backbone.Collection} [collection]
- * @return {Backbone.Collection}
- */
- _prepareCollection: function( collection ) {
- if ( this.related ) {
- this.stopListening( this.related );
- }
-
- if ( !collection || !( collection instanceof Backbone.Collection ) ) {
- var options = _.isFunction( this.options.collectionOptions ) ?
- this.options.collectionOptions( this.instance ) : this.options.collectionOptions;
-
- collection = new this.collectionType( null, options );
- }
-
- collection.model = this.relatedModel;
-
- if ( this.options.collectionKey ) {
- var key = this.options.collectionKey === true ? this.options.reverseRelation.key : this.options.collectionKey;
-
- if ( collection[ key ] && collection[ key ] !== this.instance ) {
- if ( Backbone.Relational.showWarnings && typeof console !== 'undefined' ) {
- console.warn( 'Relation=%o; collectionKey=%s already exists on collection=%o', this, key, this.options.collectionKey );
- }
- }
- else if ( key ) {
- collection[ key ] = this.instance;
- }
- }
-
- this.listenTo( collection, 'relational:add', this.handleAddition )
- .listenTo( collection, 'relational:remove', this.handleRemoval )
- .listenTo( collection, 'relational:reset', this.handleReset );
-
- return collection;
- },
-
- /**
- * Find related Models.
- * @param {Object} [options]
- * @return {Backbone.Collection}
- */
- findRelated: function( options ) {
- var related = null;
-
- options = _.defaults( { parse: this.options.parse }, options );
-
- // Replace 'this.related' by 'this.keyContents' if it is a Backbone.Collection
- if ( this.keyContents instanceof Backbone.Collection ) {
- this._prepareCollection( this.keyContents );
- related = this.keyContents;
- }
- // Otherwise, 'this.keyContents' should be an array of related object ids.
- // Re-use the current 'this.related' if it is a Backbone.Collection; otherwise, create a new collection.
- else {
- var toAdd = [];
-
- _.each( this.keyContents, function( attributes ) {
- if ( attributes instanceof this.relatedModel ) {
- var model = attributes;
- }
- else {
- // If `merge` is true, update models here, instead of during update.
- model = this.relatedModel.findOrCreate( attributes,
- _.extend( { merge: true }, options, { create: this.options.createModels } )
- );
- }
-
- model && toAdd.push( model );
- }, this );
-
- if ( this.related instanceof Backbone.Collection ) {
- related = this.related;
- }
- else {
- related = this._prepareCollection();
- }
-
- // By now, both `merge` and `parse` will already have been executed for models if they were specified.
- // Disable them to prevent additional calls.
- related.set( toAdd, _.defaults( { merge: false, parse: false }, options ) );
- }
-
- // Remove entries from `keyIds` that were already part of the relation (and are thus 'unchanged')
- this.keyIds = _.difference( this.keyIds, _.pluck( related.models, 'id' ) );
-
- return related;
- },
-
- /**
- * Normalize and reduce `keyContents` to a list of `ids`, for easier comparison
- * @param {String|Number|String[]|Number[]|Backbone.Collection} keyContents
- */
- setKeyContents: function( keyContents ) {
- this.keyContents = keyContents instanceof Backbone.Collection ? keyContents : null;
- this.keyIds = [];
-
- if ( !this.keyContents && ( keyContents || keyContents === 0 ) ) { // since 0 can be a valid `id` as well
- // Handle cases the an API/user supplies just an Object/id instead of an Array
- this.keyContents = _.isArray( keyContents ) ? keyContents : [ keyContents ];
-
- _.each( this.keyContents, function( item ) {
- var itemId = Backbone.Relational.store.resolveIdForItem( this.relatedModel, item );
- if ( itemId || itemId === 0 ) {
- this.keyIds.push( itemId );
- }
- }, this );
- }
- },
-
- /**
- * Event handler for `change:`.
- * If the contents of the key are changed, notify old & new reverse relations and initialize the new relation.
- */
- onChange: function( model, attr, options ) {
- options = options ? _.clone( options ) : {};
- this.setKeyContents( attr );
- this.changed = false;
-
- var related = this.findRelated( options );
- this.setRelated( related );
-
- if ( !options.silent ) {
- var dit = this;
- Backbone.Relational.eventQueue.add( function() {
- // The `changed` flag can be set in `handleAddition` or `handleRemoval`
- if ( dit.changed ) {
- dit.instance.trigger( 'change:' + dit.key, dit.instance, dit.related, options, true );
- dit.changed = false;
- }
- });
- }
- },
-
- /**
- * When a model is added to a 'HasMany', trigger 'add' on 'this.instance' and notify reverse relations.
- * (should be 'HasOne', must set 'this.instance' as their related).
- */
- handleAddition: function( model, coll, options ) {
- //console.debug('handleAddition called; args=%o', arguments);
- options = options ? _.clone( options ) : {};
- this.changed = true;
-
- _.each( this.getReverseRelations( model ), function( relation ) {
- relation.addRelated( this.instance, options );
- }, this );
-
- // Only trigger 'add' once the newly added model is initialized (so, has its relations set up)
- var dit = this;
- !options.silent && Backbone.Relational.eventQueue.add( function() {
- dit.instance.trigger( 'add:' + dit.key, model, dit.related, options );
- });
- },
-
- /**
- * When a model is removed from a 'HasMany', trigger 'remove' on 'this.instance' and notify reverse relations.
- * (should be 'HasOne', which should be nullified)
- */
- handleRemoval: function( model, coll, options ) {
- //console.debug('handleRemoval called; args=%o', arguments);
- options = options ? _.clone( options ) : {};
- this.changed = true;
-
- _.each( this.getReverseRelations( model ), function( relation ) {
- relation.removeRelated( this.instance, null, options );
- }, this );
-
- var dit = this;
- !options.silent && Backbone.Relational.eventQueue.add( function() {
- dit.instance.trigger( 'remove:' + dit.key, model, dit.related, options );
- });
- },
-
- handleReset: function( coll, options ) {
- var dit = this;
- options = options ? _.clone( options ) : {};
- !options.silent && Backbone.Relational.eventQueue.add( function() {
- dit.instance.trigger( 'reset:' + dit.key, dit.related, options );
- });
- },
-
- tryAddRelated: function( model, coll, options ) {
- var item = _.contains( this.keyIds, model.id );
-
- if ( item ) {
- this.addRelated( model, options );
- this.keyIds = _.without( this.keyIds, model.id );
- }
- },
-
- addRelated: function( model, options ) {
- // Allow 'model' to set up its relations before proceeding.
- // (which can result in a call to 'addRelated' from a relation of 'model')
- var dit = this;
- model.queue( function() {
- if ( dit.related && !dit.related.get( model ) ) {
- dit.related.add( model, _.defaults( { parse: false }, options ) );
- }
- });
- },
-
- removeRelated: function( model, coll, options ) {
- if ( this.related.get( model ) ) {
- this.related.remove( model, options );
- }
- }
- });
-
- /**
- * A type of Backbone.Model that also maintains relations to other models and collections.
- * New events when compared to the original:
- * - 'add:' (model, related collection, options)
- * - 'remove:' (model, related collection, options)
- * - 'change:' (model, related model or collection, options)
- */
- Backbone.RelationalModel = Backbone.Model.extend({
- relations: null, // Relation descriptions on the prototype
- _relations: null, // Relation instances
- _isInitialized: false,
- _deferProcessing: false,
- _queue: null,
- _attributeChangeFired: false, // Keeps track of `change` event firing under some conditions (like nested `set`s)
-
- subModelTypeAttribute: 'type',
- subModelTypes: null,
-
- constructor: function( attributes, options ) {
- // Nasty hack, for cases like 'model.get( ).add( item )'.
- // Defer 'processQueue', so that when 'Relation.createModels' is used we trigger 'HasMany'
- // collection events only after the model is really fully set up.
- // Example: event for "p.on( 'add:jobs' )" -> "p.get('jobs').add( { company: c.id, person: p.id } )".
- if ( options && options.collection ) {
- var dit = this,
- collection = this.collection = options.collection;
-
- // Prevent `collection` from cascading down to nested models; they shouldn't go into this `if` clause.
- delete options.collection;
-
- this._deferProcessing = true;
-
- var processQueue = function( model ) {
- if ( model === dit ) {
- dit._deferProcessing = false;
- dit.processQueue();
- collection.off( 'relational:add', processQueue );
- }
- };
- collection.on( 'relational:add', processQueue );
-
- // So we do process the queue eventually, regardless of whether this model actually gets added to 'options.collection'.
- _.defer( function() {
- processQueue( dit );
- });
- }
-
- Backbone.Relational.store.processOrphanRelations();
-
- this._queue = new Backbone.BlockingQueue();
- this._queue.block();
- Backbone.Relational.eventQueue.block();
-
- try {
- Backbone.Model.apply( this, arguments );
- }
- finally {
- // Try to run the global queue holding external events
- Backbone.Relational.eventQueue.unblock();
- }
- },
-
- /**
- * Override 'trigger' to queue 'change' and 'change:*' events
- */
- trigger: function( eventName ) {
- if ( eventName.length > 5 && eventName.indexOf( 'change' ) === 0 ) {
- var dit = this,
- args = arguments;
-
- Backbone.Relational.eventQueue.add( function() {
- if ( !dit._isInitialized ) {
- return;
- }
-
- // Determine if the `change` event is still valid, now that all relations are populated
- var changed = true;
- if ( eventName === 'change' ) {
- // `hasChanged` may have gotten reset by nested calls to `set`.
- changed = dit.hasChanged() || dit._attributeChangeFired;
- dit._attributeChangeFired = false;
- }
- else {
- var attr = eventName.slice( 7 ),
- rel = dit.getRelation( attr );
-
- if ( rel ) {
- // If `attr` is a relation, `change:attr` get triggered from `Relation.onChange`.
- // These take precedence over `change:attr` events triggered by `Model.set`.
- // The relation sets a fourth attribute to `true`. If this attribute is present,
- // continue triggering this event; otherwise, it's from `Model.set` and should be stopped.
- changed = ( args[ 4 ] === true );
-
- // If this event was triggered by a relation, set the right value in `this.changed`
- // (a Collection or Model instead of raw data).
- if ( changed ) {
- dit.changed[ attr ] = args[ 2 ];
- }
- // Otherwise, this event is from `Model.set`. If the relation doesn't report a change,
- // remove attr from `dit.changed` so `hasChanged` doesn't take it into account.
- else if ( !rel.changed ) {
- delete dit.changed[ attr ];
- }
- }
- else if ( changed ) {
- dit._attributeChangeFired = true;
- }
- }
-
- changed && Backbone.Model.prototype.trigger.apply( dit, args );
- });
- }
- else {
- Backbone.Model.prototype.trigger.apply( this, arguments );
- }
-
- return this;
- },
-
- /**
- * Initialize Relations present in this.relations; determine the type (HasOne/HasMany), then creates a new instance.
- * Invoked in the first call so 'set' (which is made from the Backbone.Model constructor).
- */
- initializeRelations: function( options ) {
- this.acquire(); // Setting up relations often also involve calls to 'set', and we only want to enter this function once
- this._relations = {};
-
- _.each( _.result( this, 'relations' ) || [], function( rel ) {
- Backbone.Relational.store.initializeRelation( this, rel, options );
- }, this );
-
- this._isInitialized = true;
- this.release();
- this.processQueue();
- },
-
- /**
- * When new values are set, notify this model's relations (also if options.silent is set).
- * (called from `set`; Relation.setRelated locks this model before calling 'set' on it to prevent loops)
- * @param {Object} [changedAttrs]
- * @param {Object} [options]
- */
- updateRelations: function( changedAttrs, options ) {
- if ( this._isInitialized && !this.isLocked() ) {
- _.each( this._relations, function( rel ) {
- if ( !changedAttrs || ( rel.keySource in changedAttrs || rel.key in changedAttrs ) ) {
- // Fetch data in `rel.keySource` if data got set in there, or `rel.key` otherwise
- var value = this.attributes[ rel.keySource ] || this.attributes[ rel.key ],
- attr = changedAttrs && ( changedAttrs[ rel.keySource ] || changedAttrs[ rel.key ] );
-
- // Update a relation if its value differs from this model's attributes, or it's been explicitly nullified.
- // Which can also happen before the originally intended related model has been found (`val` is null).
- if ( rel.related !== value || ( value === null && attr === null ) ) {
- this.trigger( 'relational:change:' + rel.key, this, value, options || {} );
- }
- }
-
- // Explicitly clear 'keySource', to prevent a leaky abstraction if 'keySource' differs from 'key'.
- if ( rel.keySource !== rel.key ) {
- delete this.attributes[ rel.keySource ];
- }
- }, this );
- }
- },
-
- /**
- * Either add to the queue (if we're not initialized yet), or execute right away.
- */
- queue: function( func ) {
- this._queue.add( func );
- },
-
- /**
- * Process _queue
- */
- processQueue: function() {
- if ( this._isInitialized && !this._deferProcessing && this._queue.isBlocked() ) {
- this._queue.unblock();
- }
- },
-
- /**
- * Get a specific relation.
- * @param {string} key The relation key to look for.
- * @return {Backbone.Relation} An instance of 'Backbone.Relation', if a relation was found for 'key', or null.
- */
- getRelation: function( key ) {
- return this._relations[ key ];
- },
-
- /**
- * Get all of the created relations.
- * @return {Backbone.Relation[]}
- */
- getRelations: function() {
- return _.values( this._relations );
- },
-
- /**
- * Retrieve related objects.
- * @param {string} key The relation key to fetch models for.
- * @param {Object} [options] Options for 'Backbone.Model.fetch' and 'Backbone.sync'.
- * @param {Boolean} [refresh=false] Fetch existing models from the server as well (in order to update them).
- * @return {jQuery.when[]} An array of request objects
- */
- fetchRelated: function( key, options, refresh ) {
- // Set default `options` for fetch
- options = _.extend( { update: true, remove: false }, options );
-
- var models,
- setUrl,
- requests = [],
- rel = this.getRelation( key ),
- idsToFetch = rel && ( ( rel.keyIds && rel.keyIds.slice( 0 ) ) || ( ( rel.keyId || rel.keyId === 0 ) ? [ rel.keyId ] : [] ) );
-
- // On `refresh`, add the ids for current models in the relation to `idsToFetch`
- if ( refresh ) {
- models = rel.related instanceof Backbone.Collection ? rel.related.models : [ rel.related ];
- _.each( models, function( model ) {
- if ( model.id || model.id === 0 ) {
- idsToFetch.push( model.id );
- }
- });
- }
-
- if ( idsToFetch && idsToFetch.length ) {
- // Find (or create) a model for each one that is to be fetched
- var created = [];
- models = _.map( idsToFetch, function( id ) {
- var model = Backbone.Relational.store.find( rel.relatedModel, id );
-
- if ( !model ) {
- var attrs = {};
- attrs[ rel.relatedModel.prototype.idAttribute ] = id;
- model = rel.relatedModel.findOrCreate( attrs, options );
- created.push( model );
- }
-
- return model;
- }, this );
-
- // Try if the 'collection' can provide a url to fetch a set of models in one request.
- if ( rel.related instanceof Backbone.Collection && _.isFunction( rel.related.url ) ) {
- setUrl = rel.related.url( models );
- }
-
- // An assumption is that when 'Backbone.Collection.url' is a function, it can handle building of set urls.
- // To make sure it can, test if the url we got by supplying a list of models to fetch is different from
- // the one supplied for the default fetch action (without args to 'url').
- if ( setUrl && setUrl !== rel.related.url() ) {
- var opts = _.defaults(
- {
- error: function() {
- var args = arguments;
- _.each( created, function( model ) {
- model.trigger( 'destroy', model, model.collection, options );
- options.error && options.error.apply( model, args );
- });
- },
- url: setUrl
- },
- options
- );
-
- requests = [ rel.related.fetch( opts ) ];
- }
- else {
- requests = _.map( models, function( model ) {
- var opts = _.defaults(
- {
- error: function() {
- if ( _.contains( created, model ) ) {
- model.trigger( 'destroy', model, model.collection, options );
- options.error && options.error.apply( model, arguments );
- }
- }
- },
- options
- );
- return model.fetch( opts );
- }, this );
- }
- }
-
- return requests;
- },
-
- get: function( attr ) {
- var originalResult = Backbone.Model.prototype.get.call( this, attr );
-
- // Use `originalResult` get if dotNotation not enabled or not required because no dot is in `attr`
- if ( !this.dotNotation || attr.indexOf( '.' ) === -1 ) {
- return originalResult;
- }
-
- // Go through all splits and return the final result
- var splits = attr.split( '.' );
- var result = _.reduce(splits, function( model, split ) {
- if ( _.isNull(model) || _.isUndefined( model ) ) {
- // Return undefined if the path cannot be expanded
- return undefined;
- }
- else if ( model instanceof Backbone.Model ) {
- return Backbone.Model.prototype.get.call( model, split );
- }
- else if ( model instanceof Backbone.Collection ) {
- return Backbone.Collection.prototype.at.call( model, split )
- }
- else {
- throw new Error( 'Attribute must be an instanceof Backbone.Model or Backbone.Collection. Is: ' + model + ', currentSplit: ' + split );
- }
- }, this );
-
- if ( originalResult !== undefined && result !== undefined ) {
- throw new Error( "Ambiguous result for '" + attr + "'. direct result: " + originalResult + ", dotNotation: " + result );
- }
-
- return originalResult || result;
- },
-
- set: function( key, value, options ) {
- Backbone.Relational.eventQueue.block();
-
- // Duplicate backbone's behavior to allow separate key/value parameters, instead of a single 'attributes' object
- var attributes;
- if ( _.isObject( key ) || key == null ) {
- attributes = key;
- options = value;
- }
- else {
- attributes = {};
- attributes[ key ] = value;
- }
-
- try {
- var id = this.id,
- newId = attributes && this.idAttribute in attributes && attributes[ this.idAttribute ];
-
- // Check if we're not setting a duplicate id before actually calling `set`.
- Backbone.Relational.store.checkId( this, newId );
-
- var result = Backbone.Model.prototype.set.apply( this, arguments );
-
- // Ideal place to set up relations, if this is the first time we're here for this model
- if ( !this._isInitialized && !this.isLocked() ) {
- this.constructor.initializeModelHierarchy();
- Backbone.Relational.store.register( this );
- this.initializeRelations( options );
- }
- // The store should know about an `id` update asap
- else if ( newId && newId !== id ) {
- Backbone.Relational.store.update( this );
- }
-
- if ( attributes ) {
- this.updateRelations( attributes, options );
- }
- }
- finally {
- // Try to run the global queue holding external events
- Backbone.Relational.eventQueue.unblock();
- }
-
- return result;
- },
-
- clone: function() {
- var attributes = _.clone( this.attributes );
- if ( !_.isUndefined( attributes[ this.idAttribute ] ) ) {
- attributes[ this.idAttribute ] = null;
- }
-
- _.each( this.getRelations(), function( rel ) {
- delete attributes[ rel.key ];
- });
-
- return new this.constructor( attributes );
- },
-
- /**
- * Convert relations to JSON, omits them when required
- */
- toJSON: function( options ) {
- // If this Model has already been fully serialized in this branch once, return to avoid loops
- if ( this.isLocked() ) {
- return this.id;
- }
-
- this.acquire();
- var json = Backbone.Model.prototype.toJSON.call( this, options );
-
- if ( this.constructor._superModel && !( this.constructor._subModelTypeAttribute in json ) ) {
- json[ this.constructor._subModelTypeAttribute ] = this.constructor._subModelTypeValue;
- }
-
- _.each( this._relations, function( rel ) {
- var related = json[ rel.key ],
- includeInJSON = rel.options.includeInJSON,
- value = null;
-
- if ( includeInJSON === true ) {
- if ( related && _.isFunction( related.toJSON ) ) {
- value = related.toJSON( options );
- }
- }
- else if ( _.isString( includeInJSON ) ) {
- if ( related instanceof Backbone.Collection ) {
- value = related.pluck( includeInJSON );
- }
- else if ( related instanceof Backbone.Model ) {
- value = related.get( includeInJSON );
- }
-
- // Add ids for 'unfound' models if includeInJSON is equal to (only) the relatedModel's `idAttribute`
- if ( includeInJSON === rel.relatedModel.prototype.idAttribute ) {
- if ( rel instanceof Backbone.HasMany ) {
- value = value.concat( rel.keyIds );
- }
- else if ( rel instanceof Backbone.HasOne ) {
- value = value || rel.keyId;
-
- if ( !value && !_.isObject( rel.keyContents ) ) {
- value = rel.keyContents || null;
- }
- }
- }
- }
- else if ( _.isArray( includeInJSON ) ) {
- if ( related instanceof Backbone.Collection ) {
- value = [];
- related.each( function( model ) {
- var curJson = {};
- _.each( includeInJSON, function( key ) {
- curJson[ key ] = model.get( key );
- });
- value.push( curJson );
- });
- }
- else if ( related instanceof Backbone.Model ) {
- value = {};
- _.each( includeInJSON, function( key ) {
- value[ key ] = related.get( key );
- });
- }
- }
- else {
- delete json[ rel.key ];
- }
-
- if ( includeInJSON ) {
- json[ rel.keyDestination ] = value;
- }
-
- if ( rel.keyDestination !== rel.key ) {
- delete json[ rel.key ];
- }
- });
-
- this.release();
- return json;
- }
- },
- {
- /**
- *
- * @param superModel
- * @returns {Backbone.RelationalModel.constructor}
- */
- setup: function( superModel ) {
- // We don't want to share a relations array with a parent, as this will cause problems with
- // reverse relations.
- this.prototype.relations = ( this.prototype.relations || [] ).slice( 0 );
-
- this._subModels = {};
- this._superModel = null;
-
- // If this model has 'subModelTypes' itself, remember them in the store
- if ( this.prototype.hasOwnProperty( 'subModelTypes' ) ) {
- Backbone.Relational.store.addSubModels( this.prototype.subModelTypes, this );
- }
- // The 'subModelTypes' property should not be inherited, so reset it.
- else {
- this.prototype.subModelTypes = null;
- }
-
- // Initialize all reverseRelations that belong to this new model.
- _.each( this.prototype.relations || [], function( rel ) {
- if ( !rel.model ) {
- rel.model = this;
- }
-
- if ( rel.reverseRelation && rel.model === this ) {
- var preInitialize = true;
- if ( _.isString( rel.relatedModel ) ) {
- /**
- * The related model might not be defined for two reasons
- * 1. it is related to itself
- * 2. it never gets defined, e.g. a typo
- * 3. the model hasn't been defined yet, but will be later
- * In neither of these cases do we need to pre-initialize reverse relations.
- * However, for 3. (which is, to us, indistinguishable from 2.), we do need to attempt
- * setting up this relation again later, in case the related model is defined later.
- */
- var relatedModel = Backbone.Relational.store.getObjectByName( rel.relatedModel );
- preInitialize = relatedModel && ( relatedModel.prototype instanceof Backbone.RelationalModel );
- }
-
- if ( preInitialize ) {
- Backbone.Relational.store.initializeRelation( null, rel );
- }
- else if ( _.isString( rel.relatedModel ) ) {
- Backbone.Relational.store.addOrphanRelation( rel );
- }
- }
- }, this );
-
- return this;
- },
-
- /**
- * Create a 'Backbone.Model' instance based on 'attributes'.
- * @param {Object} attributes
- * @param {Object} [options]
- * @return {Backbone.Model}
- */
- build: function( attributes, options ) {
- // 'build' is a possible entrypoint; it's possible no model hierarchy has been determined yet.
- this.initializeModelHierarchy();
-
- // Determine what type of (sub)model should be built if applicable.
- var model = this._findSubModelType( this, attributes ) || this;
-
- return new model( attributes, options );
- },
-
- /**
- * Determines what type of (sub)model should be built if applicable.
- * Looks up the proper subModelType in 'this._subModels', recursing into
- * types until a match is found. Returns the applicable 'Backbone.Model'
- * or null if no match is found.
- * @param {Backbone.Model} type
- * @param {Object} attributes
- * @return {Backbone.Model}
- */
- _findSubModelType: function( type, attributes ) {
- if ( type._subModels && type.prototype.subModelTypeAttribute in attributes ) {
- var subModelTypeAttribute = attributes[ type.prototype.subModelTypeAttribute ];
- var subModelType = type._subModels[ subModelTypeAttribute ];
- if ( subModelType ) {
- return subModelType;
- }
- else {
- // Recurse into subModelTypes to find a match
- for ( subModelTypeAttribute in type._subModels ) {
- subModelType = this._findSubModelType( type._subModels[ subModelTypeAttribute ], attributes );
- if ( subModelType ) {
- return subModelType;
- }
- }
- }
- }
- return null;
- },
-
- /**
- *
- */
- initializeModelHierarchy: function() {
- // Inherit any relations that have been defined in the parent model.
- this.inheritRelations();
-
- // If we came here through 'build' for a model that has 'subModelTypes' then try to initialize the ones that
- // haven't been resolved yet.
- if ( this.prototype.subModelTypes ) {
- var resolvedSubModels = _.keys( this._subModels );
- var unresolvedSubModels = _.omit( this.prototype.subModelTypes, resolvedSubModels );
- _.each( unresolvedSubModels, function( subModelTypeName ) {
- var subModelType = Backbone.Relational.store.getObjectByName( subModelTypeName );
- subModelType && subModelType.initializeModelHierarchy();
- });
- }
- },
-
- inheritRelations: function() {
- // Bail out if we've been here before.
- if (!_.isUndefined( this._superModel ) && !_.isNull( this._superModel )) {
- return;
- }
- // Try to initialize the _superModel.
- Backbone.Relational.store.setupSuperModel( this );
-
- // If a superModel has been found, copy relations from the _superModel if they haven't been inherited automatically
- // (due to a redefinition of 'relations').
- if ( this._superModel ) {
- // The _superModel needs a chance to initialize its own inherited relations before we attempt to inherit relations
- // from the _superModel. You don't want to call 'initializeModelHierarchy' because that could cause sub-models of
- // this class to inherit their relations before this class has had chance to inherit it's relations.
- this._superModel.inheritRelations();
- if ( this._superModel.prototype.relations ) {
- // Find relations that exist on the '_superModel', but not yet on this model.
- var inheritedRelations = _.select( this._superModel.prototype.relations || [], function( superRel ) {
- return !_.any( this.prototype.relations || [], function( rel ) {
- return superRel.relatedModel === rel.relatedModel && superRel.key === rel.key;
- }, this );
- }, this );
-
- this.prototype.relations = inheritedRelations.concat( this.prototype.relations );
- }
- }
- // Otherwise, make sure we don't get here again for this type by making '_superModel' false so we fail the
- // isUndefined/isNull check next time.
- else {
- this._superModel = false;
- }
- },
-
- /**
- * Find an instance of `this` type in 'Backbone.Relational.store'.
- * - If `attributes` is a string or a number, `findOrCreate` will just query the `store` and return a model if found.
- * - If `attributes` is an object and is found in the store, the model will be updated with `attributes` unless `options.update` is `false`.
- * Otherwise, a new model is created with `attributes` (unless `options.create` is explicitly set to `false`).
- * @param {Object|String|Number} attributes Either a model's id, or the attributes used to create or update a model.
- * @param {Object} [options]
- * @param {Boolean} [options.create=true]
- * @param {Boolean} [options.merge=true]
- * @param {Boolean} [options.parse=false]
- * @return {Backbone.RelationalModel}
- */
- findOrCreate: function( attributes, options ) {
- options || ( options = {} );
- var parsedAttributes = ( _.isObject( attributes ) && options.parse && this.prototype.parse ) ?
- this.prototype.parse( _.clone( attributes ) ) : attributes;
-
- // Try to find an instance of 'this' model type in the store
- var model = Backbone.Relational.store.find( this, parsedAttributes );
-
- // If we found an instance, update it with the data in 'item' (unless 'options.merge' is false).
- // If not, create an instance (unless 'options.create' is false).
- if ( _.isObject( attributes ) ) {
- if ( model && options.merge !== false ) {
- // Make sure `options.collection` and `options.url` doesn't cascade to nested models
- delete options.collection;
- delete options.url;
-
- model.set( parsedAttributes, options );
- }
- else if ( !model && options.create !== false ) {
- model = this.build( attributes, options );
- }
- }
-
- return model;
- },
-
- /**
- * Find an instance of `this` type in 'Backbone.Relational.store'.
- * - If `attributes` is a string or a number, `find` will just query the `store` and return a model if found.
- * - If `attributes` is an object and is found in the store, the model will be updated with `attributes` unless `options.update` is `false`.
- * @param {Object|String|Number} attributes Either a model's id, or the attributes used to create or update a model.
- * @param {Object} [options]
- * @param {Boolean} [options.merge=true]
- * @param {Boolean} [options.parse=false]
- * @return {Backbone.RelationalModel}
- */
- find: function( attributes, options ) {
- options || ( options = {} );
- options.create = false;
- return this.findOrCreate( attributes, options );
- }
- });
- _.extend( Backbone.RelationalModel.prototype, Backbone.Semaphore );
-
- /**
- * Override Backbone.Collection._prepareModel, so objects will be built using the correct type
- * if the collection.model has subModels.
- * Attempts to find a model for `attrs` in Backbone.store through `findOrCreate`
- * (which sets the new properties on it if found), or instantiates a new model.
- */
- Backbone.Collection.prototype.__prepareModel = Backbone.Collection.prototype._prepareModel;
- Backbone.Collection.prototype._prepareModel = function ( attrs, options ) {
- var model;
-
- if ( attrs instanceof Backbone.Model ) {
- if ( !attrs.collection ) {
- attrs.collection = this;
- }
- model = attrs;
- }
- else {
- options = options ? _.clone( options ) : {};
- options.collection = this;
-
- if ( typeof this.model.findOrCreate !== 'undefined' ) {
- model = this.model.findOrCreate( attrs, options );
- }
- else {
- model = new this.model( attrs, options );
- }
-
- if ( model && model.validationError ) {
- this.trigger( 'invalid', this, attrs, options );
- model = false;
- }
- }
-
- return model;
- };
-
-
- /**
- * Override Backbone.Collection.set, so we'll create objects from attributes where required,
- * and update the existing models. Also, trigger 'relational:add'.
- */
- var set = Backbone.Collection.prototype.__set = Backbone.Collection.prototype.set;
- Backbone.Collection.prototype.set = function( models, options ) {
- // Short-circuit if this Collection doesn't hold RelationalModels
- if ( !( this.model.prototype instanceof Backbone.RelationalModel ) ) {
- return set.apply( this, arguments );
- }
-
- if ( options && options.parse ) {
- models = this.parse( models, options );
- }
-
- models = _.isArray( models ) ? models.slice() : ( models ? [ models ] : [] );
-
- var newModels = [],
- toAdd = [];
-
- //console.debug( 'calling add on coll=%o; model=%o, options=%o', this, models, options );
- _.each( models, function( model ) {
- if ( !( model instanceof Backbone.Model ) ) {
- model = Backbone.Collection.prototype._prepareModel.call( this, model, options );
- }
-
- if ( model ) {
- toAdd.push( model );
-
- if ( !( this.get( model ) || this.get( model.cid ) ) ) {
- newModels.push( model );
- }
- // If we arrive in `add` while performing a `set` (after a create, so the model gains an `id`),
- // we may get here before `_onModelEvent` has had the chance to update `_byId`.
- else if ( model.id != null ) {
- this._byId[ model.id ] = model;
- }
- }
- }, this );
-
- // Add 'models' in a single batch, so the original add will only be called once (and thus 'sort', etc).
- // If `parse` was specified, the collection and contained models have been parsed now.
- var result = set.call( this, toAdd, _.defaults( { parse: false }, options ) );
-
- _.each( newModels, function( model ) {
- // Fire a `relational:add` event for any model in `newModels` that has actually been added to the collection.
- if ( this.get( model ) || this.get( model.cid ) ) {
- this.trigger( 'relational:add', model, this, options );
- }
- }, this );
-
- return result;
- };
-
- /**
- * Override 'Backbone.Collection.remove' to trigger 'relational:remove'.
- */
- var remove = Backbone.Collection.prototype.__remove = Backbone.Collection.prototype.remove;
- Backbone.Collection.prototype.remove = function( models, options ) {
- // Short-circuit if this Collection doesn't hold RelationalModels
- if ( !( this.model.prototype instanceof Backbone.RelationalModel ) ) {
- return remove.apply( this, arguments );
- }
-
- models = _.isArray( models ) ? models.slice() : [ models ];
- options || ( options = {} );
-
- var toRemove = [];
-
- //console.debug('calling remove on coll=%o; models=%o, options=%o', this, models, options );
- _.each( models, function( model ) {
- model = this.get( model ) || ( model && this.get( model.cid ) );
- model && toRemove.push( model );
- }, this );
-
- var result = remove.call( this, toRemove, options );
-
- _.each( toRemove, function( model ) {
- this.trigger('relational:remove', model, this, options);
- }, this );
-
- return result;
- };
-
- /**
- * Override 'Backbone.Collection.reset' to trigger 'relational:reset'.
- */
- var reset = Backbone.Collection.prototype.__reset = Backbone.Collection.prototype.reset;
- Backbone.Collection.prototype.reset = function( models, options ) {
- options = _.extend( { merge: true }, options );
- var result = reset.call( this, models, options );
-
- if ( this.model.prototype instanceof Backbone.RelationalModel ) {
- this.trigger( 'relational:reset', this, options );
- }
-
- return result;
- };
-
- /**
- * Override 'Backbone.Collection.sort' to trigger 'relational:reset'.
- */
- var sort = Backbone.Collection.prototype.__sort = Backbone.Collection.prototype.sort;
- Backbone.Collection.prototype.sort = function( options ) {
- var result = sort.call( this, options );
-
- if ( this.model.prototype instanceof Backbone.RelationalModel ) {
- this.trigger( 'relational:reset', this, options );
- }
-
- return result;
- };
-
- /**
- * Override 'Backbone.Collection.trigger' so 'add', 'remove' and 'reset' events are queued until relations
- * are ready.
- */
- var trigger = Backbone.Collection.prototype.__trigger = Backbone.Collection.prototype.trigger;
- Backbone.Collection.prototype.trigger = function( eventName ) {
- // Short-circuit if this Collection doesn't hold RelationalModels
- if ( !( this.model.prototype instanceof Backbone.RelationalModel ) ) {
- return trigger.apply( this, arguments );
- }
-
- if ( eventName === 'add' || eventName === 'remove' || eventName === 'reset' || eventName === 'sort' ) {
- var dit = this,
- args = arguments;
-
- if ( _.isObject( args[ 3 ] ) ) {
- args = _.toArray( args );
- // the fourth argument is the option object.
- // we need to clone it, as it could be modified while we wait on the eventQueue to be unblocked
- args[ 3 ] = _.clone( args[ 3 ] );
- }
-
- Backbone.Relational.eventQueue.add( function() {
- trigger.apply( dit, args );
- });
- }
- else {
- trigger.apply( this, arguments );
- }
-
- return this;
- };
-
- // Override .extend() to automatically call .setup()
- Backbone.RelationalModel.extend = function( protoProps, classProps ) {
- var child = Backbone.Model.extend.apply( this, arguments );
-
- child.setup( this );
-
- return child;
- };
-})();
\ No newline at end of file
diff --git a/static/js/lib/bootstrap-editable.js b/static/js/lib/bootstrap-editable.js
index 7c5ae1c..4df64c5 100644
--- a/static/js/lib/bootstrap-editable.js
+++ b/static/js/lib/bootstrap-editable.js
@@ -2746,7 +2746,7 @@ List - abstract class for inputs that have source option loaded from js array or
Since 1.4.1 key `children` supported to render OPTGROUP (for **select** input only).
`[{text: "group1", children: [{value: 1, text: "text1"}, {value: 2, text: "text2"}]}, ...]`
-
+
@property source
@type string | array | object | function
@default null
@@ -4691,1236 +4691,1236 @@ Editableform based on Twitter Bootstrap
(function( $ ) {
- function UTCDate(){
- return new Date(Date.UTC.apply(Date, arguments));
- }
- function UTCToday(){
- var today = new Date();
- return UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
- }
-
- // Picker object
-
- var Datepicker = function(element, options) {
- var that = this;
-
- this._process_options(options);
-
- this.element = $(element);
- this.isInline = false;
- this.isInput = this.element.is('input');
- this.component = this.element.is('.date') ? this.element.find('.add-on, .btn') : false;
- this.hasInput = this.component && this.element.find('input').length;
- if(this.component && this.component.length === 0)
- this.component = false;
-
- this.picker = $(DPGlobal.template);
- this._buildEvents();
- this._attachEvents();
-
- if(this.isInline) {
- this.picker.addClass('datepicker-inline').appendTo(this.element);
- } else {
- this.picker.addClass('datepicker-dropdown dropdown-menu');
- }
-
- if (this.o.rtl){
- this.picker.addClass('datepicker-rtl');
- this.picker.find('.prev i, .next i')
- .toggleClass('icon-arrow-left icon-arrow-right');
- }
-
-
- this.viewMode = this.o.startView;
-
- if (this.o.calendarWeeks)
- this.picker.find('tfoot th.today')
- .attr('colspan', function(i, val){
- return parseInt(val) + 1;
- });
-
- this._allow_update = false;
-
- this.setStartDate(this.o.startDate);
- this.setEndDate(this.o.endDate);
- this.setDaysOfWeekDisabled(this.o.daysOfWeekDisabled);
-
- this.fillDow();
- this.fillMonths();
-
- this._allow_update = true;
-
- this.update();
- this.showMode();
-
- if(this.isInline) {
- this.show();
- }
- };
-
- Datepicker.prototype = {
- constructor: Datepicker,
-
- _process_options: function(opts){
- // Store raw options for reference
- this._o = $.extend({}, this._o, opts);
- // Processed options
- var o = this.o = $.extend({}, this._o);
-
- // Check if "de-DE" style date is available, if not language should
- // fallback to 2 letter code eg "de"
- var lang = o.language;
- if (!dates[lang]) {
- lang = lang.split('-')[0];
- if (!dates[lang])
- lang = defaults.language;
- }
- o.language = lang;
-
- switch(o.startView){
- case 2:
- case 'decade':
- o.startView = 2;
- break;
- case 1:
- case 'year':
- o.startView = 1;
- break;
- default:
- o.startView = 0;
- }
-
- switch (o.minViewMode) {
- case 1:
- case 'months':
- o.minViewMode = 1;
- break;
- case 2:
- case 'years':
- o.minViewMode = 2;
- break;
- default:
- o.minViewMode = 0;
- }
-
- o.startView = Math.max(o.startView, o.minViewMode);
-
- o.weekStart %= 7;
- o.weekEnd = ((o.weekStart + 6) % 7);
-
- var format = DPGlobal.parseFormat(o.format)
- if (o.startDate !== -Infinity) {
- o.startDate = DPGlobal.parseDate(o.startDate, format, o.language);
- }
- if (o.endDate !== Infinity) {
- o.endDate = DPGlobal.parseDate(o.endDate, format, o.language);
- }
-
- o.daysOfWeekDisabled = o.daysOfWeekDisabled||[];
- if (!$.isArray(o.daysOfWeekDisabled))
- o.daysOfWeekDisabled = o.daysOfWeekDisabled.split(/[,\s]*/);
- o.daysOfWeekDisabled = $.map(o.daysOfWeekDisabled, function (d) {
- return parseInt(d, 10);
- });
- },
- _events: [],
- _secondaryEvents: [],
- _applyEvents: function(evs){
- for (var i=0, el, ev; i this.o.endDate) {
- this.viewDate = new Date(this.o.endDate);
- } else {
- this.viewDate = new Date(this.date);
- }
- this.fill();
- },
-
- fillDow: function(){
- var dowCnt = this.o.weekStart,
- html = '';
- if(this.o.calendarWeeks){
- var cell = '| | ';
- html += cell;
- this.picker.find('.datepicker-days thead tr:first-child').prepend(cell);
- }
- while (dowCnt < this.o.weekStart + 7) {
- html += ''+dates[this.o.language].daysMin[(dowCnt++)%7]+' | ';
- }
- html += '
';
- this.picker.find('.datepicker-days thead').append(html);
- },
-
- fillMonths: function(){
- var html = '',
- i = 0;
- while (i < 12) {
- html += ''+dates[this.o.language].monthsShort[i++]+'';
- }
- this.picker.find('.datepicker-months td').html(html);
- },
-
- setRange: function(range){
- if (!range || !range.length)
- delete this.range;
- else
- this.range = $.map(range, function(d){ return d.valueOf(); });
- this.fill();
- },
-
- getClassNames: function(date){
- var cls = [],
- year = this.viewDate.getUTCFullYear(),
- month = this.viewDate.getUTCMonth(),
- currentDate = this.date.valueOf(),
- today = new Date();
- if (date.getUTCFullYear() < year || (date.getUTCFullYear() == year && date.getUTCMonth() < month)) {
- cls.push('old');
- } else if (date.getUTCFullYear() > year || (date.getUTCFullYear() == year && date.getUTCMonth() > month)) {
- cls.push('new');
- }
- // Compare internal UTC date with local today, not UTC today
- if (this.o.todayHighlight &&
- date.getUTCFullYear() == today.getFullYear() &&
- date.getUTCMonth() == today.getMonth() &&
- date.getUTCDate() == today.getDate()) {
- cls.push('today');
- }
- if (currentDate && date.valueOf() == currentDate) {
- cls.push('active');
- }
- if (date.valueOf() < this.o.startDate || date.valueOf() > this.o.endDate ||
- $.inArray(date.getUTCDay(), this.o.daysOfWeekDisabled) !== -1) {
- cls.push('disabled');
- }
- if (this.range){
- if (date > this.range[0] && date < this.range[this.range.length-1]){
- cls.push('range');
- }
- if ($.inArray(date.valueOf(), this.range) != -1){
- cls.push('selected');
- }
- }
- return cls;
- },
-
- fill: function() {
- var d = new Date(this.viewDate),
- year = d.getUTCFullYear(),
- month = d.getUTCMonth(),
- startYear = this.o.startDate !== -Infinity ? this.o.startDate.getUTCFullYear() : -Infinity,
- startMonth = this.o.startDate !== -Infinity ? this.o.startDate.getUTCMonth() : -Infinity,
- endYear = this.o.endDate !== Infinity ? this.o.endDate.getUTCFullYear() : Infinity,
- endMonth = this.o.endDate !== Infinity ? this.o.endDate.getUTCMonth() : Infinity,
- currentDate = this.date && this.date.valueOf(),
- tooltip;
- this.picker.find('.datepicker-days thead th.datepicker-switch')
- .text(dates[this.o.language].months[month]+' '+year);
- this.picker.find('tfoot th.today')
- .text(dates[this.o.language].today)
- .toggle(this.o.todayBtn !== false);
- this.picker.find('tfoot th.clear')
- .text(dates[this.o.language].clear)
- .toggle(this.o.clearBtn !== false);
- this.updateNavArrows();
- this.fillMonths();
- var prevMonth = UTCDate(year, month-1, 28,0,0,0,0),
- day = DPGlobal.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
- prevMonth.setUTCDate(day);
- prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.o.weekStart + 7)%7);
- var nextMonth = new Date(prevMonth);
- nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
- nextMonth = nextMonth.valueOf();
- var html = [];
- var clsName;
- while(prevMonth.valueOf() < nextMonth) {
- if (prevMonth.getUTCDay() == this.o.weekStart) {
- html.push('');
- if(this.o.calendarWeeks){
- // ISO 8601: First week contains first thursday.
- // ISO also states week starts on Monday, but we can be more abstract here.
- var
- // Start of current week: based on weekstart/current date
- ws = new Date(+prevMonth + (this.o.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
- // Thursday of this week
- th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
- // First Thursday of year, year from thursday
- yth = new Date(+(yth = UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
- // Calendar week: ms between thursdays, div ms per day, div 7 days
- calWeek = (th - yth) / 864e5 / 7 + 1;
- html.push('| '+ calWeek +' | ');
-
- }
- }
- clsName = this.getClassNames(prevMonth);
- clsName.push('day');
-
- var before = this.o.beforeShowDay(prevMonth);
- if (before === undefined)
- before = {};
- else if (typeof(before) === 'boolean')
- before = {enabled: before};
- else if (typeof(before) === 'string')
- before = {classes: before};
- if (before.enabled === false)
- clsName.push('disabled');
- if (before.classes)
- clsName = clsName.concat(before.classes.split(/\s+/));
- if (before.tooltip)
- tooltip = before.tooltip;
-
- clsName = $.unique(clsName);
- html.push(''+prevMonth.getUTCDate() + ' | ');
- if (prevMonth.getUTCDay() == this.o.weekEnd) {
- html.push('
');
- }
- prevMonth.setUTCDate(prevMonth.getUTCDate()+1);
- }
- this.picker.find('.datepicker-days tbody').empty().append(html.join(''));
- var currentYear = this.date && this.date.getUTCFullYear();
-
- var months = this.picker.find('.datepicker-months')
- .find('th:eq(1)')
- .text(year)
- .end()
- .find('span').removeClass('active');
- if (currentYear && currentYear == year) {
- months.eq(this.date.getUTCMonth()).addClass('active');
- }
- if (year < startYear || year > endYear) {
- months.addClass('disabled');
- }
- if (year == startYear) {
- months.slice(0, startMonth).addClass('disabled');
- }
- if (year == endYear) {
- months.slice(endMonth+1).addClass('disabled');
- }
-
- html = '';
- year = parseInt(year/10, 10) * 10;
- var yearCont = this.picker.find('.datepicker-years')
- .find('th:eq(1)')
- .text(year + '-' + (year + 9))
- .end()
- .find('td');
- year -= 1;
- for (var i = -1; i < 11; i++) {
- html += ''+year+'';
- year += 1;
- }
- yearCont.html(html);
- },
-
- updateNavArrows: function() {
- if (!this._allow_update) return;
-
- var d = new Date(this.viewDate),
- year = d.getUTCFullYear(),
- month = d.getUTCMonth();
- switch (this.viewMode) {
- case 0:
- if (this.o.startDate !== -Infinity && year <= this.o.startDate.getUTCFullYear() && month <= this.o.startDate.getUTCMonth()) {
- this.picker.find('.prev').css({visibility: 'hidden'});
- } else {
- this.picker.find('.prev').css({visibility: 'visible'});
- }
- if (this.o.endDate !== Infinity && year >= this.o.endDate.getUTCFullYear() && month >= this.o.endDate.getUTCMonth()) {
- this.picker.find('.next').css({visibility: 'hidden'});
- } else {
- this.picker.find('.next').css({visibility: 'visible'});
- }
- break;
- case 1:
- case 2:
- if (this.o.startDate !== -Infinity && year <= this.o.startDate.getUTCFullYear()) {
- this.picker.find('.prev').css({visibility: 'hidden'});
- } else {
- this.picker.find('.prev').css({visibility: 'visible'});
- }
- if (this.o.endDate !== Infinity && year >= this.o.endDate.getUTCFullYear()) {
- this.picker.find('.next').css({visibility: 'hidden'});
- } else {
- this.picker.find('.next').css({visibility: 'visible'});
- }
- break;
- }
- },
-
- click: function(e) {
- e.preventDefault();
- var target = $(e.target).closest('span, td, th');
- if (target.length == 1) {
- switch(target[0].nodeName.toLowerCase()) {
- case 'th':
- switch(target[0].className) {
- case 'datepicker-switch':
- this.showMode(1);
- break;
- case 'prev':
- case 'next':
- var dir = DPGlobal.modes[this.viewMode].navStep * (target[0].className == 'prev' ? -1 : 1);
- switch(this.viewMode){
- case 0:
- this.viewDate = this.moveMonth(this.viewDate, dir);
- break;
- case 1:
- case 2:
- this.viewDate = this.moveYear(this.viewDate, dir);
- break;
- }
- this.fill();
- break;
- case 'today':
- var date = new Date();
- date = UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
-
- this.showMode(-2);
- var which = this.o.todayBtn == 'linked' ? null : 'view';
- this._setDate(date, which);
- break;
- case 'clear':
- var element;
- if (this.isInput)
- element = this.element;
- else if (this.component)
- element = this.element.find('input');
- if (element)
- element.val("").change();
- this._trigger('changeDate');
- this.update();
- if (this.o.autoclose)
- this.hide();
- break;
- }
- break;
- case 'span':
- if (!target.is('.disabled')) {
- this.viewDate.setUTCDate(1);
- if (target.is('.month')) {
- var day = 1;
- var month = target.parent().find('span').index(target);
- var year = this.viewDate.getUTCFullYear();
- this.viewDate.setUTCMonth(month);
- this._trigger('changeMonth', this.viewDate);
- if (this.o.minViewMode === 1) {
- this._setDate(UTCDate(year, month, day,0,0,0,0));
- }
- } else {
- var year = parseInt(target.text(), 10)||0;
- var day = 1;
- var month = 0;
- this.viewDate.setUTCFullYear(year);
- this._trigger('changeYear', this.viewDate);
- if (this.o.minViewMode === 2) {
- this._setDate(UTCDate(year, month, day,0,0,0,0));
- }
- }
- this.showMode(-1);
- this.fill();
- }
- break;
- case 'td':
- if (target.is('.day') && !target.is('.disabled')){
- var day = parseInt(target.text(), 10)||1;
- var year = this.viewDate.getUTCFullYear(),
- month = this.viewDate.getUTCMonth();
- 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;
- }
- }
- this._setDate(UTCDate(year, month, day,0,0,0,0));
- }
- break;
- }
- }
- },
-
- _setDate: function(date, which){
- if (!which || which == 'date')
- this.date = new Date(date);
- if (!which || which == 'view')
- this.viewDate = new Date(date);
- this.fill();
- this.setValue();
- this._trigger('changeDate');
- var element;
- if (this.isInput) {
- element = this.element;
- } else if (this.component){
- element = this.element.find('input');
- }
- if (element) {
- element.change();
- if (this.o.autoclose && (!which || which == 'date')) {
- this.hide();
- }
- }
- },
-
- moveMonth: function(date, dir){
- if (!dir) return date;
- var new_date = new Date(date.valueOf()),
- day = new_date.getUTCDate(),
- month = new_date.getUTCMonth(),
- mag = Math.abs(dir),
- new_month, test;
- dir = dir > 0 ? 1 : -1;
- if (mag == 1){
- test = dir == -1
- // If going back one month, make sure month is not current month
- // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
- ? function(){ return new_date.getUTCMonth() == month; }
- // If going forward one month, make sure month is as expected
- // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
- : function(){ return new_date.getUTCMonth() != new_month; };
- new_month = month + dir;
- new_date.setUTCMonth(new_month);
- // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
- if (new_month < 0 || new_month > 11)
- new_month = (new_month + 12) % 12;
- } else {
- // For magnitudes >1, move one month at a time...
- for (var i=0; i= this.o.startDate && date <= this.o.endDate;
- },
-
- keydown: function(e){
- if (this.picker.is(':not(:visible)')){
- if (e.keyCode == 27) // allow escape to hide and re-show picker
- this.show();
- return;
- }
- var dateChanged = false,
- dir, day, month,
- newDate, newViewDate;
- switch(e.keyCode){
- case 27: // escape
- this.hide();
- e.preventDefault();
- break;
- case 37: // left
- case 39: // right
- if (!this.o.keyboardNavigation) break;
- dir = e.keyCode == 37 ? -1 : 1;
- if (e.ctrlKey){
- newDate = this.moveYear(this.date, dir);
- newViewDate = this.moveYear(this.viewDate, dir);
- } else if (e.shiftKey){
- newDate = this.moveMonth(this.date, dir);
- newViewDate = this.moveMonth(this.viewDate, dir);
- } else {
- newDate = new Date(this.date);
- newDate.setUTCDate(this.date.getUTCDate() + dir);
- newViewDate = new Date(this.viewDate);
- newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
- }
- if (this.dateWithinRange(newDate)){
- this.date = newDate;
- this.viewDate = newViewDate;
- this.setValue();
- this.update();
- e.preventDefault();
- dateChanged = true;
- }
- break;
- case 38: // up
- case 40: // down
- if (!this.o.keyboardNavigation) break;
- dir = e.keyCode == 38 ? -1 : 1;
- if (e.ctrlKey){
- newDate = this.moveYear(this.date, dir);
- newViewDate = this.moveYear(this.viewDate, dir);
- } else if (e.shiftKey){
- newDate = this.moveMonth(this.date, dir);
- newViewDate = this.moveMonth(this.viewDate, dir);
- } else {
- newDate = new Date(this.date);
- newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
- newViewDate = new Date(this.viewDate);
- newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
- }
- if (this.dateWithinRange(newDate)){
- this.date = newDate;
- this.viewDate = newViewDate;
- this.setValue();
- this.update();
- e.preventDefault();
- dateChanged = true;
- }
- break;
- case 13: // enter
- this.hide();
- e.preventDefault();
- break;
- case 9: // tab
- this.hide();
- break;
- }
- if (dateChanged){
- this._trigger('changeDate');
- var element;
- if (this.isInput) {
- element = this.element;
- } else if (this.component){
- element = this.element.find('input');
- }
- if (element) {
- element.change();
- }
- }
- },
-
- showMode: function(dir) {
- if (dir) {
- this.viewMode = Math.max(this.o.minViewMode, Math.min(2, this.viewMode + dir));
- }
- /*
- vitalets: fixing bug of very special conditions:
- jquery 1.7.1 + webkit + show inline datepicker in bootstrap popover.
- Method show() does not set display css correctly and datepicker is not shown.
- Changed to .css('display', 'block') solve the problem.
- See https://github.com/vitalets/x-editable/issues/37
-
- In jquery 1.7.2+ everything works fine.
- */
- //this.picker.find('>div').hide().filter('.datepicker-'+DPGlobal.modes[this.viewMode].clsName).show();
- this.picker.find('>div').hide().filter('.datepicker-'+DPGlobal.modes[this.viewMode].clsName).css('display', 'block');
- this.updateNavArrows();
- }
- };
-
- var DateRangePicker = function(element, options){
- this.element = $(element);
- this.inputs = $.map(options.inputs, function(i){ return i.jquery ? i[0] : i; });
- delete options.inputs;
-
- $(this.inputs)
- .datepicker(options)
- .bind('changeDate', $.proxy(this.dateUpdated, this));
-
- this.pickers = $.map(this.inputs, function(i){ return $(i).data('datepicker'); });
- this.updateDates();
- };
- DateRangePicker.prototype = {
- updateDates: function(){
- this.dates = $.map(this.pickers, function(i){ return i.date; });
- this.updateRanges();
- },
- updateRanges: function(){
- var range = $.map(this.dates, function(d){ return d.valueOf(); });
- $.each(this.pickers, function(i, p){
- p.setRange(range);
- });
- },
- dateUpdated: function(e){
- var dp = $(e.target).data('datepicker'),
- new_date = dp.getUTCDate(),
- i = $.inArray(e.target, this.inputs),
- l = this.inputs.length;
- if (i == -1) return;
-
- if (new_date < this.dates[i]){
- // Date being moved earlier/left
- while (i>=0 && new_date < this.dates[i]){
- this.pickers[i--].setUTCDate(new_date);
- }
- }
- else if (new_date > this.dates[i]){
- // Date being moved later/right
- while (i this.dates[i]){
- this.pickers[i++].setUTCDate(new_date);
- }
- }
- this.updateDates();
- },
- remove: function(){
- $.map(this.pickers, function(p){ p.remove(); });
- delete this.element.data().datepicker;
- }
- };
-
- function opts_from_el(el, prefix){
- // Derive options from element data-attrs
- var data = $(el).data(),
- out = {}, inkey,
- replace = new RegExp('^' + prefix.toLowerCase() + '([A-Z])'),
- prefix = new RegExp('^' + prefix.toLowerCase());
- for (var key in data)
- if (prefix.test(key)){
- inkey = key.replace(replace, function(_,a){ return a.toLowerCase(); });
- out[inkey] = data[key];
- }
- return out;
- }
-
- function opts_from_locale(lang){
- // Derive options from locale plugins
- var out = {};
- // Check if "de-DE" style date is available, if not language should
- // fallback to 2 letter code eg "de"
- if (!dates[lang]) {
- lang = lang.split('-')[0]
- if (!dates[lang])
- return;
- }
- var d = dates[lang];
- $.each(locale_opts, function(i,k){
- if (k in d)
- out[k] = d[k];
- });
- return out;
- }
-
- var old = $.fn.datepicker;
- var datepicker = $.fn.datepicker = function ( option ) {
- var args = Array.apply(null, arguments);
- args.shift();
- var internal_return,
- this_return;
- this.each(function () {
- var $this = $(this),
- data = $this.data('datepicker'),
- options = typeof option == 'object' && option;
- if (!data) {
- var elopts = opts_from_el(this, 'date'),
- // Preliminary otions
- xopts = $.extend({}, defaults, elopts, options),
- locopts = opts_from_locale(xopts.language),
- // Options priority: js args, data-attrs, locales, defaults
- opts = $.extend({}, defaults, locopts, elopts, options);
- if ($this.is('.input-daterange') || opts.inputs){
- var ropts = {
- inputs: opts.inputs || $this.find('input').toArray()
- };
- $this.data('datepicker', (data = new DateRangePicker(this, $.extend(opts, ropts))));
- }
- else{
- $this.data('datepicker', (data = new Datepicker(this, opts)));
- }
- }
- if (typeof option == 'string' && typeof data[option] == 'function') {
- internal_return = data[option].apply(data, args);
- if (internal_return !== undefined)
- return false;
- }
- });
- if (internal_return !== undefined)
- return internal_return;
- else
- return this;
- };
-
- var defaults = $.fn.datepicker.defaults = {
- autoclose: false,
- beforeShowDay: $.noop,
- calendarWeeks: false,
- clearBtn: false,
- daysOfWeekDisabled: [],
- endDate: Infinity,
- forceParse: true,
- format: 'mm/dd/yyyy',
- keyboardNavigation: true,
- language: 'en',
- minViewMode: 0,
- rtl: false,
- startDate: -Infinity,
- startView: 0,
- todayBtn: false,
- todayHighlight: false,
- weekStart: 0
- };
- var locale_opts = $.fn.datepicker.locale_opts = [
- 'format',
- 'rtl',
- 'weekStart'
- ];
- $.fn.datepicker.Constructor = Datepicker;
- var dates = $.fn.datepicker.dates = {
- en: {
- days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
- daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
- daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
- months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
- monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
- today: "Today",
- clear: "Clear"
- }
- };
-
- var DPGlobal = {
- modes: [
- {
- clsName: 'days',
- navFnc: 'Month',
- navStep: 1
- },
- {
- clsName: 'months',
- navFnc: 'FullYear',
- navStep: 1
- },
- {
- clsName: 'years',
- navFnc: 'FullYear',
- navStep: 10
- }],
- isLeapYear: function (year) {
- return (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0));
- },
- getDaysInMonth: function (year, month) {
- return [31, (DPGlobal.isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month];
- },
- validParts: /dd?|DD?|mm?|MM?|yy(?:yy)?/g,
- nonpunctuation: /[^ -\/:-@\[\u3400-\u9fff-`{-~\t\n\r]+/g,
- parseFormat: function(format){
- // IE treats \0 as a string end in inputs (truncating the value),
- // so it's a bad format delimiter, anyway
- var separators = format.replace(this.validParts, '\0').split('\0'),
- parts = format.match(this.validParts);
- if (!separators || !separators.length || !parts || parts.length === 0){
- throw new Error("Invalid date format.");
- }
- return {separators: separators, parts: parts};
- },
- parseDate: function(date, format, language) {
- if (date instanceof Date) return date;
- if (typeof format === 'string')
- format = DPGlobal.parseFormat(format);
- if (/^[\-+]\d+[dmwy]([\s,]+[\-+]\d+[dmwy])*$/.test(date)) {
- var part_re = /([\-+]\d+)([dmwy])/,
- parts = date.match(/([\-+]\d+)([dmwy])/g),
- part, dir;
- date = new Date();
- for (var i=0; i'+
- ''+
- ' | '+
- ' | '+
- ' | '+
- '
'+
- '',
- contTemplate: ' |
',
- footTemplate: ' |
|---|
|
'
- };
- DPGlobal.template = ''+
- '
'+
- '
'+
- DPGlobal.headTemplate+
- ''+
- DPGlobal.footTemplate+
- '
'+
- '
'+
- '
'+
- '
'+
- DPGlobal.headTemplate+
- DPGlobal.contTemplate+
- DPGlobal.footTemplate+
- '
'+
- '
'+
- '
'+
- '
'+
- DPGlobal.headTemplate+
- DPGlobal.contTemplate+
- DPGlobal.footTemplate+
- '
'+
- '
'+
- '
';
-
- $.fn.datepicker.DPGlobal = DPGlobal;
-
-
- /* DATEPICKER NO CONFLICT
- * =================== */
-
- $.fn.datepicker.noConflict = function(){
- $.fn.datepicker = old;
- return this;
- };
-
-
- /* DATEPICKER DATA-API
- * ================== */
-
- $(document).on(
- 'focus.datepicker.data-api click.datepicker.data-api',
- '[data-provide="datepicker"]',
- function(e){
- var $this = $(this);
- if ($this.data('datepicker')) return;
- e.preventDefault();
- // component click requires us to explicitly show it
- datepicker.call($this, 'show');
- }
- );
- $(function(){
- //$('[data-provide="datepicker-inline"]').datepicker();
+ function UTCDate(){
+ return new Date(Date.UTC.apply(Date, arguments));
+ }
+ function UTCToday(){
+ var today = new Date();
+ return UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
+ }
+
+ // Picker object
+
+ var Datepicker = function(element, options) {
+ var that = this;
+
+ this._process_options(options);
+
+ this.element = $(element);
+ this.isInline = false;
+ this.isInput = this.element.is('input');
+ this.component = this.element.is('.date') ? this.element.find('.add-on, .btn') : false;
+ this.hasInput = this.component && this.element.find('input').length;
+ if(this.component && this.component.length === 0)
+ this.component = false;
+
+ this.picker = $(DPGlobal.template);
+ this._buildEvents();
+ this._attachEvents();
+
+ if(this.isInline) {
+ this.picker.addClass('datepicker-inline').appendTo(this.element);
+ } else {
+ this.picker.addClass('datepicker-dropdown dropdown-menu');
+ }
+
+ if (this.o.rtl){
+ this.picker.addClass('datepicker-rtl');
+ this.picker.find('.prev i, .next i')
+ .toggleClass('icon-arrow-left icon-arrow-right');
+ }
+
+
+ this.viewMode = this.o.startView;
+
+ if (this.o.calendarWeeks)
+ this.picker.find('tfoot th.today')
+ .attr('colspan', function(i, val){
+ return parseInt(val) + 1;
+ });
+
+ this._allow_update = false;
+
+ this.setStartDate(this.o.startDate);
+ this.setEndDate(this.o.endDate);
+ this.setDaysOfWeekDisabled(this.o.daysOfWeekDisabled);
+
+ this.fillDow();
+ this.fillMonths();
+
+ this._allow_update = true;
+
+ this.update();
+ this.showMode();
+
+ if(this.isInline) {
+ this.show();
+ }
+ };
+
+ Datepicker.prototype = {
+ constructor: Datepicker,
+
+ _process_options: function(opts){
+ // Store raw options for reference
+ this._o = $.extend({}, this._o, opts);
+ // Processed options
+ var o = this.o = $.extend({}, this._o);
+
+ // Check if "de-DE" style date is available, if not language should
+ // fallback to 2 letter code eg "de"
+ var lang = o.language;
+ if (!dates[lang]) {
+ lang = lang.split('-')[0];
+ if (!dates[lang])
+ lang = defaults.language;
+ }
+ o.language = lang;
+
+ switch(o.startView){
+ case 2:
+ case 'decade':
+ o.startView = 2;
+ break;
+ case 1:
+ case 'year':
+ o.startView = 1;
+ break;
+ default:
+ o.startView = 0;
+ }
+
+ switch (o.minViewMode) {
+ case 1:
+ case 'months':
+ o.minViewMode = 1;
+ break;
+ case 2:
+ case 'years':
+ o.minViewMode = 2;
+ break;
+ default:
+ o.minViewMode = 0;
+ }
+
+ o.startView = Math.max(o.startView, o.minViewMode);
+
+ o.weekStart %= 7;
+ o.weekEnd = ((o.weekStart + 6) % 7);
+
+ var format = DPGlobal.parseFormat(o.format)
+ if (o.startDate !== -Infinity) {
+ o.startDate = DPGlobal.parseDate(o.startDate, format, o.language);
+ }
+ if (o.endDate !== Infinity) {
+ o.endDate = DPGlobal.parseDate(o.endDate, format, o.language);
+ }
+
+ o.daysOfWeekDisabled = o.daysOfWeekDisabled||[];
+ if (!$.isArray(o.daysOfWeekDisabled))
+ o.daysOfWeekDisabled = o.daysOfWeekDisabled.split(/[,\s]*/);
+ o.daysOfWeekDisabled = $.map(o.daysOfWeekDisabled, function (d) {
+ return parseInt(d, 10);
+ });
+ },
+ _events: [],
+ _secondaryEvents: [],
+ _applyEvents: function(evs){
+ for (var i=0, el, ev; i this.o.endDate) {
+ this.viewDate = new Date(this.o.endDate);
+ } else {
+ this.viewDate = new Date(this.date);
+ }
+ this.fill();
+ },
+
+ fillDow: function(){
+ var dowCnt = this.o.weekStart,
+ html = '';
+ if(this.o.calendarWeeks){
+ var cell = '| | ';
+ html += cell;
+ this.picker.find('.datepicker-days thead tr:first-child').prepend(cell);
+ }
+ while (dowCnt < this.o.weekStart + 7) {
+ html += ''+dates[this.o.language].daysMin[(dowCnt++)%7]+' | ';
+ }
+ html += '
';
+ this.picker.find('.datepicker-days thead').append(html);
+ },
+
+ fillMonths: function(){
+ var html = '',
+ i = 0;
+ while (i < 12) {
+ html += ''+dates[this.o.language].monthsShort[i++]+'';
+ }
+ this.picker.find('.datepicker-months td').html(html);
+ },
+
+ setRange: function(range){
+ if (!range || !range.length)
+ delete this.range;
+ else
+ this.range = $.map(range, function(d){ return d.valueOf(); });
+ this.fill();
+ },
+
+ getClassNames: function(date){
+ var cls = [],
+ year = this.viewDate.getUTCFullYear(),
+ month = this.viewDate.getUTCMonth(),
+ currentDate = this.date.valueOf(),
+ today = new Date();
+ if (date.getUTCFullYear() < year || (date.getUTCFullYear() == year && date.getUTCMonth() < month)) {
+ cls.push('old');
+ } else if (date.getUTCFullYear() > year || (date.getUTCFullYear() == year && date.getUTCMonth() > month)) {
+ cls.push('new');
+ }
+ // Compare internal UTC date with local today, not UTC today
+ if (this.o.todayHighlight &&
+ date.getUTCFullYear() == today.getFullYear() &&
+ date.getUTCMonth() == today.getMonth() &&
+ date.getUTCDate() == today.getDate()) {
+ cls.push('today');
+ }
+ if (currentDate && date.valueOf() == currentDate) {
+ cls.push('active');
+ }
+ if (date.valueOf() < this.o.startDate || date.valueOf() > this.o.endDate ||
+ $.inArray(date.getUTCDay(), this.o.daysOfWeekDisabled) !== -1) {
+ cls.push('disabled');
+ }
+ if (this.range){
+ if (date > this.range[0] && date < this.range[this.range.length-1]){
+ cls.push('range');
+ }
+ if ($.inArray(date.valueOf(), this.range) != -1){
+ cls.push('selected');
+ }
+ }
+ return cls;
+ },
+
+ fill: function() {
+ var d = new Date(this.viewDate),
+ year = d.getUTCFullYear(),
+ month = d.getUTCMonth(),
+ startYear = this.o.startDate !== -Infinity ? this.o.startDate.getUTCFullYear() : -Infinity,
+ startMonth = this.o.startDate !== -Infinity ? this.o.startDate.getUTCMonth() : -Infinity,
+ endYear = this.o.endDate !== Infinity ? this.o.endDate.getUTCFullYear() : Infinity,
+ endMonth = this.o.endDate !== Infinity ? this.o.endDate.getUTCMonth() : Infinity,
+ currentDate = this.date && this.date.valueOf(),
+ tooltip;
+ this.picker.find('.datepicker-days thead th.datepicker-switch')
+ .text(dates[this.o.language].months[month]+' '+year);
+ this.picker.find('tfoot th.today')
+ .text(dates[this.o.language].today)
+ .toggle(this.o.todayBtn !== false);
+ this.picker.find('tfoot th.clear')
+ .text(dates[this.o.language].clear)
+ .toggle(this.o.clearBtn !== false);
+ this.updateNavArrows();
+ this.fillMonths();
+ var prevMonth = UTCDate(year, month-1, 28,0,0,0,0),
+ day = DPGlobal.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
+ prevMonth.setUTCDate(day);
+ prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.o.weekStart + 7)%7);
+ var nextMonth = new Date(prevMonth);
+ nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
+ nextMonth = nextMonth.valueOf();
+ var html = [];
+ var clsName;
+ while(prevMonth.valueOf() < nextMonth) {
+ if (prevMonth.getUTCDay() == this.o.weekStart) {
+ html.push('');
+ if(this.o.calendarWeeks){
+ // ISO 8601: First week contains first thursday.
+ // ISO also states week starts on Monday, but we can be more abstract here.
+ var
+ // Start of current week: based on weekstart/current date
+ ws = new Date(+prevMonth + (this.o.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
+ // Thursday of this week
+ th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
+ // First Thursday of year, year from thursday
+ yth = new Date(+(yth = UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
+ // Calendar week: ms between thursdays, div ms per day, div 7 days
+ calWeek = (th - yth) / 864e5 / 7 + 1;
+ html.push('| '+ calWeek +' | ');
+
+ }
+ }
+ clsName = this.getClassNames(prevMonth);
+ clsName.push('day');
+
+ var before = this.o.beforeShowDay(prevMonth);
+ if (before === undefined)
+ before = {};
+ else if (typeof(before) === 'boolean')
+ before = {enabled: before};
+ else if (typeof(before) === 'string')
+ before = {classes: before};
+ if (before.enabled === false)
+ clsName.push('disabled');
+ if (before.classes)
+ clsName = clsName.concat(before.classes.split(/\s+/));
+ if (before.tooltip)
+ tooltip = before.tooltip;
+
+ clsName = $.unique(clsName);
+ html.push(''+prevMonth.getUTCDate() + ' | ');
+ if (prevMonth.getUTCDay() == this.o.weekEnd) {
+ html.push('
');
+ }
+ prevMonth.setUTCDate(prevMonth.getUTCDate()+1);
+ }
+ this.picker.find('.datepicker-days tbody').empty().append(html.join(''));
+ var currentYear = this.date && this.date.getUTCFullYear();
+
+ var months = this.picker.find('.datepicker-months')
+ .find('th:eq(1)')
+ .text(year)
+ .end()
+ .find('span').removeClass('active');
+ if (currentYear && currentYear == year) {
+ months.eq(this.date.getUTCMonth()).addClass('active');
+ }
+ if (year < startYear || year > endYear) {
+ months.addClass('disabled');
+ }
+ if (year == startYear) {
+ months.slice(0, startMonth).addClass('disabled');
+ }
+ if (year == endYear) {
+ months.slice(endMonth+1).addClass('disabled');
+ }
+
+ html = '';
+ year = parseInt(year/10, 10) * 10;
+ var yearCont = this.picker.find('.datepicker-years')
+ .find('th:eq(1)')
+ .text(year + '-' + (year + 9))
+ .end()
+ .find('td');
+ year -= 1;
+ for (var i = -1; i < 11; i++) {
+ html += ''+year+'';
+ year += 1;
+ }
+ yearCont.html(html);
+ },
+
+ updateNavArrows: function() {
+ if (!this._allow_update) return;
+
+ var d = new Date(this.viewDate),
+ year = d.getUTCFullYear(),
+ month = d.getUTCMonth();
+ switch (this.viewMode) {
+ case 0:
+ if (this.o.startDate !== -Infinity && year <= this.o.startDate.getUTCFullYear() && month <= this.o.startDate.getUTCMonth()) {
+ this.picker.find('.prev').css({visibility: 'hidden'});
+ } else {
+ this.picker.find('.prev').css({visibility: 'visible'});
+ }
+ if (this.o.endDate !== Infinity && year >= this.o.endDate.getUTCFullYear() && month >= this.o.endDate.getUTCMonth()) {
+ this.picker.find('.next').css({visibility: 'hidden'});
+ } else {
+ this.picker.find('.next').css({visibility: 'visible'});
+ }
+ break;
+ case 1:
+ case 2:
+ if (this.o.startDate !== -Infinity && year <= this.o.startDate.getUTCFullYear()) {
+ this.picker.find('.prev').css({visibility: 'hidden'});
+ } else {
+ this.picker.find('.prev').css({visibility: 'visible'});
+ }
+ if (this.o.endDate !== Infinity && year >= this.o.endDate.getUTCFullYear()) {
+ this.picker.find('.next').css({visibility: 'hidden'});
+ } else {
+ this.picker.find('.next').css({visibility: 'visible'});
+ }
+ break;
+ }
+ },
+
+ click: function(e) {
+ e.preventDefault();
+ var target = $(e.target).closest('span, td, th');
+ if (target.length == 1) {
+ switch(target[0].nodeName.toLowerCase()) {
+ case 'th':
+ switch(target[0].className) {
+ case 'datepicker-switch':
+ this.showMode(1);
+ break;
+ case 'prev':
+ case 'next':
+ var dir = DPGlobal.modes[this.viewMode].navStep * (target[0].className == 'prev' ? -1 : 1);
+ switch(this.viewMode){
+ case 0:
+ this.viewDate = this.moveMonth(this.viewDate, dir);
+ break;
+ case 1:
+ case 2:
+ this.viewDate = this.moveYear(this.viewDate, dir);
+ break;
+ }
+ this.fill();
+ break;
+ case 'today':
+ var date = new Date();
+ date = UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
+
+ this.showMode(-2);
+ var which = this.o.todayBtn == 'linked' ? null : 'view';
+ this._setDate(date, which);
+ break;
+ case 'clear':
+ var element;
+ if (this.isInput)
+ element = this.element;
+ else if (this.component)
+ element = this.element.find('input');
+ if (element)
+ element.val("").change();
+ this._trigger('changeDate');
+ this.update();
+ if (this.o.autoclose)
+ this.hide();
+ break;
+ }
+ break;
+ case 'span':
+ if (!target.is('.disabled')) {
+ this.viewDate.setUTCDate(1);
+ if (target.is('.month')) {
+ var day = 1;
+ var month = target.parent().find('span').index(target);
+ var year = this.viewDate.getUTCFullYear();
+ this.viewDate.setUTCMonth(month);
+ this._trigger('changeMonth', this.viewDate);
+ if (this.o.minViewMode === 1) {
+ this._setDate(UTCDate(year, month, day,0,0,0,0));
+ }
+ } else {
+ var year = parseInt(target.text(), 10)||0;
+ var day = 1;
+ var month = 0;
+ this.viewDate.setUTCFullYear(year);
+ this._trigger('changeYear', this.viewDate);
+ if (this.o.minViewMode === 2) {
+ this._setDate(UTCDate(year, month, day,0,0,0,0));
+ }
+ }
+ this.showMode(-1);
+ this.fill();
+ }
+ break;
+ case 'td':
+ if (target.is('.day') && !target.is('.disabled')){
+ var day = parseInt(target.text(), 10)||1;
+ var year = this.viewDate.getUTCFullYear(),
+ month = this.viewDate.getUTCMonth();
+ 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;
+ }
+ }
+ this._setDate(UTCDate(year, month, day,0,0,0,0));
+ }
+ break;
+ }
+ }
+ },
+
+ _setDate: function(date, which){
+ if (!which || which == 'date')
+ this.date = new Date(date);
+ if (!which || which == 'view')
+ this.viewDate = new Date(date);
+ this.fill();
+ this.setValue();
+ this._trigger('changeDate');
+ var element;
+ if (this.isInput) {
+ element = this.element;
+ } else if (this.component){
+ element = this.element.find('input');
+ }
+ if (element) {
+ element.change();
+ if (this.o.autoclose && (!which || which == 'date')) {
+ this.hide();
+ }
+ }
+ },
+
+ moveMonth: function(date, dir){
+ if (!dir) return date;
+ var new_date = new Date(date.valueOf()),
+ day = new_date.getUTCDate(),
+ month = new_date.getUTCMonth(),
+ mag = Math.abs(dir),
+ new_month, test;
+ dir = dir > 0 ? 1 : -1;
+ if (mag == 1){
+ test = dir == -1
+ // If going back one month, make sure month is not current month
+ // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
+ ? function(){ return new_date.getUTCMonth() == month; }
+ // If going forward one month, make sure month is as expected
+ // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
+ : function(){ return new_date.getUTCMonth() != new_month; };
+ new_month = month + dir;
+ new_date.setUTCMonth(new_month);
+ // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
+ if (new_month < 0 || new_month > 11)
+ new_month = (new_month + 12) % 12;
+ } else {
+ // For magnitudes >1, move one month at a time...
+ for (var i=0; i= this.o.startDate && date <= this.o.endDate;
+ },
+
+ keydown: function(e){
+ if (this.picker.is(':not(:visible)')){
+ if (e.keyCode == 27) // allow escape to hide and re-show picker
+ this.show();
+ return;
+ }
+ var dateChanged = false,
+ dir, day, month,
+ newDate, newViewDate;
+ switch(e.keyCode){
+ case 27: // escape
+ this.hide();
+ e.preventDefault();
+ break;
+ case 37: // left
+ case 39: // right
+ if (!this.o.keyboardNavigation) break;
+ dir = e.keyCode == 37 ? -1 : 1;
+ if (e.ctrlKey){
+ newDate = this.moveYear(this.date, dir);
+ newViewDate = this.moveYear(this.viewDate, dir);
+ } else if (e.shiftKey){
+ newDate = this.moveMonth(this.date, dir);
+ newViewDate = this.moveMonth(this.viewDate, dir);
+ } else {
+ newDate = new Date(this.date);
+ newDate.setUTCDate(this.date.getUTCDate() + dir);
+ newViewDate = new Date(this.viewDate);
+ newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
+ }
+ if (this.dateWithinRange(newDate)){
+ this.date = newDate;
+ this.viewDate = newViewDate;
+ this.setValue();
+ this.update();
+ e.preventDefault();
+ dateChanged = true;
+ }
+ break;
+ case 38: // up
+ case 40: // down
+ if (!this.o.keyboardNavigation) break;
+ dir = e.keyCode == 38 ? -1 : 1;
+ if (e.ctrlKey){
+ newDate = this.moveYear(this.date, dir);
+ newViewDate = this.moveYear(this.viewDate, dir);
+ } else if (e.shiftKey){
+ newDate = this.moveMonth(this.date, dir);
+ newViewDate = this.moveMonth(this.viewDate, dir);
+ } else {
+ newDate = new Date(this.date);
+ newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
+ newViewDate = new Date(this.viewDate);
+ newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
+ }
+ if (this.dateWithinRange(newDate)){
+ this.date = newDate;
+ this.viewDate = newViewDate;
+ this.setValue();
+ this.update();
+ e.preventDefault();
+ dateChanged = true;
+ }
+ break;
+ case 13: // enter
+ this.hide();
+ e.preventDefault();
+ break;
+ case 9: // tab
+ this.hide();
+ break;
+ }
+ if (dateChanged){
+ this._trigger('changeDate');
+ var element;
+ if (this.isInput) {
+ element = this.element;
+ } else if (this.component){
+ element = this.element.find('input');
+ }
+ if (element) {
+ element.change();
+ }
+ }
+ },
+
+ showMode: function(dir) {
+ if (dir) {
+ this.viewMode = Math.max(this.o.minViewMode, Math.min(2, this.viewMode + dir));
+ }
+ /*
+ vitalets: fixing bug of very special conditions:
+ jquery 1.7.1 + webkit + show inline datepicker in bootstrap popover.
+ Method show() does not set display css correctly and datepicker is not shown.
+ Changed to .css('display', 'block') solve the problem.
+ See https://github.com/vitalets/x-editable/issues/37
+
+ In jquery 1.7.2+ everything works fine.
+ */
+ //this.picker.find('>div').hide().filter('.datepicker-'+DPGlobal.modes[this.viewMode].clsName).show();
+ this.picker.find('>div').hide().filter('.datepicker-'+DPGlobal.modes[this.viewMode].clsName).css('display', 'block');
+ this.updateNavArrows();
+ }
+ };
+
+ var DateRangePicker = function(element, options){
+ this.element = $(element);
+ this.inputs = $.map(options.inputs, function(i){ return i.jquery ? i[0] : i; });
+ delete options.inputs;
+
+ $(this.inputs)
+ .datepicker(options)
+ .bind('changeDate', $.proxy(this.dateUpdated, this));
+
+ this.pickers = $.map(this.inputs, function(i){ return $(i).data('datepicker'); });
+ this.updateDates();
+ };
+ DateRangePicker.prototype = {
+ updateDates: function(){
+ this.dates = $.map(this.pickers, function(i){ return i.date; });
+ this.updateRanges();
+ },
+ updateRanges: function(){
+ var range = $.map(this.dates, function(d){ return d.valueOf(); });
+ $.each(this.pickers, function(i, p){
+ p.setRange(range);
+ });
+ },
+ dateUpdated: function(e){
+ var dp = $(e.target).data('datepicker'),
+ new_date = dp.getUTCDate(),
+ i = $.inArray(e.target, this.inputs),
+ l = this.inputs.length;
+ if (i == -1) return;
+
+ if (new_date < this.dates[i]){
+ // Date being moved earlier/left
+ while (i>=0 && new_date < this.dates[i]){
+ this.pickers[i--].setUTCDate(new_date);
+ }
+ }
+ else if (new_date > this.dates[i]){
+ // Date being moved later/right
+ while (i this.dates[i]){
+ this.pickers[i++].setUTCDate(new_date);
+ }
+ }
+ this.updateDates();
+ },
+ remove: function(){
+ $.map(this.pickers, function(p){ p.remove(); });
+ delete this.element.data().datepicker;
+ }
+ };
+
+ function opts_from_el(el, prefix){
+ // Derive options from element data-attrs
+ var data = $(el).data(),
+ out = {}, inkey,
+ replace = new RegExp('^' + prefix.toLowerCase() + '([A-Z])'),
+ prefix = new RegExp('^' + prefix.toLowerCase());
+ for (var key in data)
+ if (prefix.test(key)){
+ inkey = key.replace(replace, function(_,a){ return a.toLowerCase(); });
+ out[inkey] = data[key];
+ }
+ return out;
+ }
+
+ function opts_from_locale(lang){
+ // Derive options from locale plugins
+ var out = {};
+ // Check if "de-DE" style date is available, if not language should
+ // fallback to 2 letter code eg "de"
+ if (!dates[lang]) {
+ lang = lang.split('-')[0]
+ if (!dates[lang])
+ return;
+ }
+ var d = dates[lang];
+ $.each(locale_opts, function(i,k){
+ if (k in d)
+ out[k] = d[k];
+ });
+ return out;
+ }
+
+ var old = $.fn.datepicker;
+ var datepicker = $.fn.datepicker = function ( option ) {
+ var args = Array.apply(null, arguments);
+ args.shift();
+ var internal_return,
+ this_return;
+ this.each(function () {
+ var $this = $(this),
+ data = $this.data('datepicker'),
+ options = typeof option == 'object' && option;
+ if (!data) {
+ var elopts = opts_from_el(this, 'date'),
+ // Preliminary otions
+ xopts = $.extend({}, defaults, elopts, options),
+ locopts = opts_from_locale(xopts.language),
+ // Options priority: js args, data-attrs, locales, defaults
+ opts = $.extend({}, defaults, locopts, elopts, options);
+ if ($this.is('.input-daterange') || opts.inputs){
+ var ropts = {
+ inputs: opts.inputs || $this.find('input').toArray()
+ };
+ $this.data('datepicker', (data = new DateRangePicker(this, $.extend(opts, ropts))));
+ }
+ else{
+ $this.data('datepicker', (data = new Datepicker(this, opts)));
+ }
+ }
+ if (typeof option == 'string' && typeof data[option] == 'function') {
+ internal_return = data[option].apply(data, args);
+ if (internal_return !== undefined)
+ return false;
+ }
+ });
+ if (internal_return !== undefined)
+ return internal_return;
+ else
+ return this;
+ };
+
+ var defaults = $.fn.datepicker.defaults = {
+ autoclose: false,
+ beforeShowDay: $.noop,
+ calendarWeeks: false,
+ clearBtn: false,
+ daysOfWeekDisabled: [],
+ endDate: Infinity,
+ forceParse: true,
+ format: 'mm/dd/yyyy',
+ keyboardNavigation: true,
+ language: 'en',
+ minViewMode: 0,
+ rtl: false,
+ startDate: -Infinity,
+ startView: 0,
+ todayBtn: false,
+ todayHighlight: false,
+ weekStart: 0
+ };
+ var locale_opts = $.fn.datepicker.locale_opts = [
+ 'format',
+ 'rtl',
+ 'weekStart'
+ ];
+ $.fn.datepicker.Constructor = Datepicker;
+ var dates = $.fn.datepicker.dates = {
+ en: {
+ days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
+ daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
+ daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
+ months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
+ monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
+ today: "Today",
+ clear: "Clear"
+ }
+ };
+
+ var DPGlobal = {
+ modes: [
+ {
+ clsName: 'days',
+ navFnc: 'Month',
+ navStep: 1
+ },
+ {
+ clsName: 'months',
+ navFnc: 'FullYear',
+ navStep: 1
+ },
+ {
+ clsName: 'years',
+ navFnc: 'FullYear',
+ navStep: 10
+ }],
+ isLeapYear: function (year) {
+ return (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0));
+ },
+ getDaysInMonth: function (year, month) {
+ return [31, (DPGlobal.isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month];
+ },
+ validParts: /dd?|DD?|mm?|MM?|yy(?:yy)?/g,
+ nonpunctuation: /[^ -\/:-@\[\u3400-\u9fff-`{-~\t\n\r]+/g,
+ parseFormat: function(format){
+ // IE treats \0 as a string end in inputs (truncating the value),
+ // so it's a bad format delimiter, anyway
+ var separators = format.replace(this.validParts, '\0').split('\0'),
+ parts = format.match(this.validParts);
+ if (!separators || !separators.length || !parts || parts.length === 0){
+ throw new Error("Invalid date format.");
+ }
+ return {separators: separators, parts: parts};
+ },
+ parseDate: function(date, format, language) {
+ if (date instanceof Date) return date;
+ if (typeof format === 'string')
+ format = DPGlobal.parseFormat(format);
+ if (/^[\-+]\d+[dmwy]([\s,]+[\-+]\d+[dmwy])*$/.test(date)) {
+ var part_re = /([\-+]\d+)([dmwy])/,
+ parts = date.match(/([\-+]\d+)([dmwy])/g),
+ part, dir;
+ date = new Date();
+ for (var i=0; i'+
+ ''+
+ ' | '+
+ ' | '+
+ ' | '+
+ '
'+
+ '',
+ contTemplate: ' |
',
+ footTemplate: ' |
|---|
|
'
+ };
+ DPGlobal.template = ''+
+ '
'+
+ '
'+
+ DPGlobal.headTemplate+
+ ''+
+ DPGlobal.footTemplate+
+ '
'+
+ '
'+
+ '
'+
+ '
'+
+ DPGlobal.headTemplate+
+ DPGlobal.contTemplate+
+ DPGlobal.footTemplate+
+ '
'+
+ '
'+
+ '
'+
+ '
'+
+ DPGlobal.headTemplate+
+ DPGlobal.contTemplate+
+ DPGlobal.footTemplate+
+ '
'+
+ '
'+
+ '
';
+
+ $.fn.datepicker.DPGlobal = DPGlobal;
+
+
+ /* DATEPICKER NO CONFLICT
+ * =================== */
+
+ $.fn.datepicker.noConflict = function(){
+ $.fn.datepicker = old;
+ return this;
+ };
+
+
+ /* DATEPICKER DATA-API
+ * ================== */
+
+ $(document).on(
+ 'focus.datepicker.data-api click.datepicker.data-api',
+ '[data-provide="datepicker"]',
+ function(e){
+ var $this = $(this);
+ if ($this.data('datepicker')) return;
+ e.preventDefault();
+ // component click requires us to explicitly show it
+ datepicker.call($this, 'show');
+ }
+ );
+ $(function(){
+ //$('[data-provide="datepicker-inline"]').datepicker();
//vit: changed to support noConflict()
datepicker.call($('[data-provide="datepicker-inline"]'));
- });
+ });
}( window.jQuery ));
diff --git a/static/js/lib/bootstrap-wysiwyg.js b/static/js/lib/bootstrap-wysiwyg.js
index 63747ff..a236085 100644
--- a/static/js/lib/bootstrap-wysiwyg.js
+++ b/static/js/lib/bootstrap-wysiwyg.js
@@ -2,205 +2,205 @@
/*global jQuery, $, FileReader*/
/*jslint browser:true*/
(function ($) {
- 'use strict';
- var readFileIntoDataUrl = function (fileInfo) {
- var loader = $.Deferred(),
- fReader = new FileReader();
- fReader.onload = function (e) {
- loader.resolve(e.target.result);
- };
- fReader.onerror = loader.reject;
- fReader.onprogress = loader.notify;
- fReader.readAsDataURL(fileInfo);
- return loader.promise();
- };
- $.fn.cleanHtml = function () {
- var html = $(this).html();
- return html && html.replace(/(
|\s|<\/div>| )*$/, '');
- };
- $.fn.wysiwyg = function (userOptions) {
- var editor = this,
- selectedRange,
- options,
- toolbarBtnSelector,
- updateToolbar = function () {
- if (options.activeToolbarClass) {
- $(options.toolbarSelector).find(toolbarBtnSelector).each(function () {
- try {
- var command = $(this).data(options.commandRole);
- if (document.queryCommandState(command)) {
- $(this).addClass(options.activeToolbarClass);
- } else {
- $(this).removeClass(options.activeToolbarClass);
- }
- } catch(e){}
- });
- }
- },
- execCommand = function (commandWithArgs, valueArg) {
- var commandArr = commandWithArgs.split(' '),
- command = commandArr.shift(),
- args = commandArr.join(' ') + (valueArg || '');
- document.execCommand(command, 0, args);
- updateToolbar();
- },
- bindHotkeys = function (hotKeys) {
- $.each(hotKeys, function (hotkey, command) {
- editor.keydown(hotkey, function (e) {
- if (editor.attr('contenteditable') && editor.is(':visible')) {
- e.preventDefault();
- e.stopPropagation();
- execCommand(command);
- }
- }).keyup(hotkey, function (e) {
- if (editor.attr('contenteditable') && editor.is(':visible')) {
- e.preventDefault();
- e.stopPropagation();
- }
- });
- });
- },
- getCurrentRange = function () {
- try {
- var sel = window.getSelection();
- if (sel.getRangeAt && sel.rangeCount) {
- return sel.getRangeAt(0);
- }
- } catch(e){}
- },
- saveSelection = function () {
- selectedRange = getCurrentRange();
- },
- restoreSelection = function () {
- try {
- var selection = window.getSelection();
- if (selectedRange) {
- try {
- selection.removeAllRanges();
- } catch (ex) {
- document.body.createTextRange().select();
- document.selection.empty();
- }
+ 'use strict';
+ var readFileIntoDataUrl = function (fileInfo) {
+ var loader = $.Deferred(),
+ fReader = new FileReader();
+ fReader.onload = function (e) {
+ loader.resolve(e.target.result);
+ };
+ fReader.onerror = loader.reject;
+ fReader.onprogress = loader.notify;
+ fReader.readAsDataURL(fileInfo);
+ return loader.promise();
+ };
+ $.fn.cleanHtml = function () {
+ var html = $(this).html();
+ return html && html.replace(/(
|\s|
<\/div>| )*$/, '');
+ };
+ $.fn.wysiwyg = function (userOptions) {
+ var editor = this,
+ selectedRange,
+ options,
+ toolbarBtnSelector,
+ updateToolbar = function () {
+ if (options.activeToolbarClass) {
+ $(options.toolbarSelector).find(toolbarBtnSelector).each(function () {
+ try {
+ var command = $(this).data(options.commandRole);
+ if (document.queryCommandState(command)) {
+ $(this).addClass(options.activeToolbarClass);
+ } else {
+ $(this).removeClass(options.activeToolbarClass);
+ }
+ } catch(e){}
+ });
+ }
+ },
+ execCommand = function (commandWithArgs, valueArg) {
+ var commandArr = commandWithArgs.split(' '),
+ command = commandArr.shift(),
+ args = commandArr.join(' ') + (valueArg || '');
+ document.execCommand(command, 0, args);
+ updateToolbar();
+ },
+ bindHotkeys = function (hotKeys) {
+ $.each(hotKeys, function (hotkey, command) {
+ editor.keydown(hotkey, function (e) {
+ if (editor.attr('contenteditable') && editor.is(':visible')) {
+ e.preventDefault();
+ e.stopPropagation();
+ execCommand(command);
+ }
+ }).keyup(hotkey, function (e) {
+ if (editor.attr('contenteditable') && editor.is(':visible')) {
+ e.preventDefault();
+ e.stopPropagation();
+ }
+ });
+ });
+ },
+ getCurrentRange = function () {
+ try {
+ var sel = window.getSelection();
+ if (sel.getRangeAt && sel.rangeCount) {
+ return sel.getRangeAt(0);
+ }
+ } catch(e){}
+ },
+ saveSelection = function () {
+ selectedRange = getCurrentRange();
+ },
+ restoreSelection = function () {
+ try {
+ var selection = window.getSelection();
+ if (selectedRange) {
+ try {
+ selection.removeAllRanges();
+ } catch (ex) {
+ document.body.createTextRange().select();
+ document.selection.empty();
+ }
- selection.addRange(selectedRange);
- }
- } catch(e){}
- },
- insertFiles = function (files) {
- editor.focus();
- $.each(files, function (idx, fileInfo) {
- if (/^image\//.test(fileInfo.type)) {
- $.when(readFileIntoDataUrl(fileInfo)).done(function (dataUrl) {
- execCommand('insertimage', dataUrl);
- }).fail(function (e) {
- options.fileUploadError("file-reader", e);
- });
- } else {
- options.fileUploadError("unsupported-file-type", fileInfo.type);
- }
- });
- },
- markSelection = function (input, color) {
- restoreSelection();
- if (document.queryCommandSupported('hiliteColor')) {
- document.execCommand('hiliteColor', 0, color || 'transparent');
- }
- saveSelection();
- input.data(options.selectionMarker, color);
- },
- bindToolbar = function (toolbar, options) {
- toolbar.find(toolbarBtnSelector).click(function () {
- restoreSelection();
- editor.focus();
- execCommand($(this).data(options.commandRole));
- saveSelection();
- });
- toolbar.find('[data-toggle=dropdown]').click(restoreSelection);
+ selection.addRange(selectedRange);
+ }
+ } catch(e){}
+ },
+ insertFiles = function (files) {
+ editor.focus();
+ $.each(files, function (idx, fileInfo) {
+ if (/^image\//.test(fileInfo.type)) {
+ $.when(readFileIntoDataUrl(fileInfo)).done(function (dataUrl) {
+ execCommand('insertimage', dataUrl);
+ }).fail(function (e) {
+ options.fileUploadError("file-reader", e);
+ });
+ } else {
+ options.fileUploadError("unsupported-file-type", fileInfo.type);
+ }
+ });
+ },
+ markSelection = function (input, color) {
+ restoreSelection();
+ if (document.queryCommandSupported('hiliteColor')) {
+ document.execCommand('hiliteColor', 0, color || 'transparent');
+ }
+ saveSelection();
+ input.data(options.selectionMarker, color);
+ },
+ bindToolbar = function (toolbar, options) {
+ toolbar.find(toolbarBtnSelector).click(function () {
+ restoreSelection();
+ editor.focus();
+ execCommand($(this).data(options.commandRole));
+ saveSelection();
+ });
+ toolbar.find('[data-toggle=dropdown]').click(restoreSelection);
- toolbar.find('input[type=text][data-' + options.commandRole + ']').on('webkitspeechchange change', function () {
- var newValue = this.value; /* ugly but prevents fake double-calls due to selection restoration */
- this.value = '';
- restoreSelection();
- if (newValue) {
- editor.focus();
- execCommand($(this).data(options.commandRole), newValue);
- }
- saveSelection();
- }).on('focus', function () {
- var input = $(this);
- if (!input.data(options.selectionMarker)) {
- markSelection(input, options.selectionColor);
- input.focus();
- }
- }).on('blur', function () {
- var input = $(this);
- if (input.data(options.selectionMarker)) {
- markSelection(input, false);
- }
- });
- toolbar.find('input[type=file][data-' + options.commandRole + ']').change(function () {
- restoreSelection();
- if (this.type === 'file' && this.files && this.files.length > 0) {
- insertFiles(this.files);
- }
- saveSelection();
- this.value = '';
- });
- },
- initFileDrops = function () {
- editor.on('dragenter dragover', false)
- .on('drop', function (e) {
- var dataTransfer = e.originalEvent.dataTransfer;
- e.stopPropagation();
- e.preventDefault();
- if (dataTransfer && dataTransfer.files && dataTransfer.files.length > 0) {
- insertFiles(dataTransfer.files);
- }
- });
- };
- options = $.extend({}, $.fn.wysiwyg.defaults, userOptions);
- toolbarBtnSelector = 'a[data-' + options.commandRole + '],button[data-' + options.commandRole + '],input[type=button][data-' + options.commandRole + ']';
- bindHotkeys(options.hotKeys);
- if (options.dragAndDropImages) {
- initFileDrops();
- }
- bindToolbar($(options.toolbarSelector), options);
- editor.attr('contenteditable', true)
- .on('mouseup keyup mouseout', function () {
- saveSelection();
- updateToolbar();
- });
- $(window).bind('touchend', function (e) {
- var isInside = (editor.is(e.target) || editor.has(e.target).length > 0),
- currentRange = getCurrentRange(),
- clear = currentRange && (currentRange.startContainer === currentRange.endContainer && currentRange.startOffset === currentRange.endOffset);
- if (!clear || isInside) {
- saveSelection();
- updateToolbar();
- }
- });
- return this;
- };
- $.fn.wysiwyg.defaults = {
- hotKeys: {
- 'ctrl+b meta+b': 'bold',
- 'ctrl+i meta+i': 'italic',
- 'ctrl+u meta+u': 'underline',
- 'ctrl+z meta+z': 'undo',
- 'ctrl+y meta+y meta+shift+z': 'redo',
- 'ctrl+l meta+l': 'justifyleft',
- 'ctrl+r meta+r': 'justifyright',
- 'ctrl+e meta+e': 'justifycenter',
- 'ctrl+j meta+j': 'justifyfull',
- 'shift+tab': 'outdent',
- 'tab': 'indent'
- },
- toolbarSelector: '[data-role=editor-toolbar]',
- commandRole: 'edit',
- activeToolbarClass: 'btn-info',
- selectionMarker: 'edit-focus-marker',
- selectionColor: 'darkgrey',
- dragAndDropImages: true,
- fileUploadError: function (reason, detail) { console.log("File upload error", reason, detail); }
- };
+ toolbar.find('input[type=text][data-' + options.commandRole + ']').on('webkitspeechchange change', function () {
+ var newValue = this.value; /* ugly but prevents fake double-calls due to selection restoration */
+ this.value = '';
+ restoreSelection();
+ if (newValue) {
+ editor.focus();
+ execCommand($(this).data(options.commandRole), newValue);
+ }
+ saveSelection();
+ }).on('focus', function () {
+ var input = $(this);
+ if (!input.data(options.selectionMarker)) {
+ markSelection(input, options.selectionColor);
+ input.focus();
+ }
+ }).on('blur', function () {
+ var input = $(this);
+ if (input.data(options.selectionMarker)) {
+ markSelection(input, false);
+ }
+ });
+ toolbar.find('input[type=file][data-' + options.commandRole + ']').change(function () {
+ restoreSelection();
+ if (this.type === 'file' && this.files && this.files.length > 0) {
+ insertFiles(this.files);
+ }
+ saveSelection();
+ this.value = '';
+ });
+ },
+ initFileDrops = function () {
+ editor.on('dragenter dragover', false)
+ .on('drop', function (e) {
+ var dataTransfer = e.originalEvent.dataTransfer;
+ e.stopPropagation();
+ e.preventDefault();
+ if (dataTransfer && dataTransfer.files && dataTransfer.files.length > 0) {
+ insertFiles(dataTransfer.files);
+ }
+ });
+ };
+ options = $.extend({}, $.fn.wysiwyg.defaults, userOptions);
+ toolbarBtnSelector = 'a[data-' + options.commandRole + '],button[data-' + options.commandRole + '],input[type=button][data-' + options.commandRole + ']';
+ bindHotkeys(options.hotKeys);
+ if (options.dragAndDropImages) {
+ initFileDrops();
+ }
+ bindToolbar($(options.toolbarSelector), options);
+ editor.attr('contenteditable', true)
+ .on('mouseup keyup mouseout', function () {
+ saveSelection();
+ updateToolbar();
+ });
+ $(window).bind('touchend', function (e) {
+ var isInside = (editor.is(e.target) || editor.has(e.target).length > 0),
+ currentRange = getCurrentRange(),
+ clear = currentRange && (currentRange.startContainer === currentRange.endContainer && currentRange.startOffset === currentRange.endOffset);
+ if (!clear || isInside) {
+ saveSelection();
+ updateToolbar();
+ }
+ });
+ return this;
+ };
+ $.fn.wysiwyg.defaults = {
+ hotKeys: {
+ 'ctrl+b meta+b': 'bold',
+ 'ctrl+i meta+i': 'italic',
+ 'ctrl+u meta+u': 'underline',
+ 'ctrl+z meta+z': 'undo',
+ 'ctrl+y meta+y meta+shift+z': 'redo',
+ 'ctrl+l meta+l': 'justifyleft',
+ 'ctrl+r meta+r': 'justifyright',
+ 'ctrl+e meta+e': 'justifycenter',
+ 'ctrl+j meta+j': 'justifyfull',
+ 'shift+tab': 'outdent',
+ 'tab': 'indent'
+ },
+ toolbarSelector: '[data-role=editor-toolbar]',
+ commandRole: 'edit',
+ activeToolbarClass: 'btn-info',
+ selectionMarker: 'edit-focus-marker',
+ selectionColor: 'darkgrey',
+ dragAndDropImages: true,
+ fileUploadError: function (reason, detail) { console.log("File upload error", reason, detail); }
+ };
}(window.jQuery));
diff --git a/static/js/lib/jquery.hotkeys.js b/static/js/lib/jquery.hotkeys.js
index f53c4b2..6a92aae 100644
--- a/static/js/lib/jquery.hotkeys.js
+++ b/static/js/lib/jquery.hotkeys.js
@@ -16,98 +16,98 @@
*/
(function(jQuery){
-
- jQuery.hotkeys = {
- version: "0.8",
+
+ jQuery.hotkeys = {
+ version: "0.8",
- specialKeys: {
- 8: "backspace", 9: "tab", 10: "return", 13: "return", 16: "shift", 17: "ctrl", 18: "alt", 19: "pause",
- 20: "capslock", 27: "esc", 32: "space", 33: "pageup", 34: "pagedown", 35: "end", 36: "home",
- 37: "left", 38: "up", 39: "right", 40: "down", 45: "insert", 46: "del",
- 96: "0", 97: "1", 98: "2", 99: "3", 100: "4", 101: "5", 102: "6", 103: "7",
- 104: "8", 105: "9", 106: "*", 107: "+", 109: "-", 110: ".", 111 : "/",
- 112: "f1", 113: "f2", 114: "f3", 115: "f4", 116: "f5", 117: "f6", 118: "f7", 119: "f8",
- 120: "f9", 121: "f10", 122: "f11", 123: "f12", 144: "numlock", 145: "scroll", 186: ";", 191: "/",
- 220: "\\", 222: "'", 224: "meta"
- },
-
- shiftNums: {
- "`": "~", "1": "!", "2": "@", "3": "#", "4": "$", "5": "%", "6": "^", "7": "&",
- "8": "*", "9": "(", "0": ")", "-": "_", "=": "+", ";": ": ", "'": "\"", ",": "<",
- ".": ">", "/": "?", "\\": "|"
- }
- };
+ specialKeys: {
+ 8: "backspace", 9: "tab", 10: "return", 13: "return", 16: "shift", 17: "ctrl", 18: "alt", 19: "pause",
+ 20: "capslock", 27: "esc", 32: "space", 33: "pageup", 34: "pagedown", 35: "end", 36: "home",
+ 37: "left", 38: "up", 39: "right", 40: "down", 45: "insert", 46: "del",
+ 96: "0", 97: "1", 98: "2", 99: "3", 100: "4", 101: "5", 102: "6", 103: "7",
+ 104: "8", 105: "9", 106: "*", 107: "+", 109: "-", 110: ".", 111 : "/",
+ 112: "f1", 113: "f2", 114: "f3", 115: "f4", 116: "f5", 117: "f6", 118: "f7", 119: "f8",
+ 120: "f9", 121: "f10", 122: "f11", 123: "f12", 144: "numlock", 145: "scroll", 186: ";", 191: "/",
+ 220: "\\", 222: "'", 224: "meta"
+ },
+
+ shiftNums: {
+ "`": "~", "1": "!", "2": "@", "3": "#", "4": "$", "5": "%", "6": "^", "7": "&",
+ "8": "*", "9": "(", "0": ")", "-": "_", "=": "+", ";": ": ", "'": "\"", ",": "<",
+ ".": ">", "/": "?", "\\": "|"
+ }
+ };
- function keyHandler( handleObj ) {
- if (typeof handleObj.data == 'undefined') return;
+ function keyHandler( handleObj ) {
+ if (typeof handleObj.data == 'undefined') return;
- if ( typeof handleObj.data === "string" ) {
- handleObj.data = { keys: handleObj.data };
- }
+ if ( typeof handleObj.data === "string" ) {
+ handleObj.data = { keys: handleObj.data };
+ }
- // Only care when a possible input has been specified
- if ( !handleObj.data || !handleObj.data.keys || typeof handleObj.data.keys !== "string" ) {
- return;
- }
+ // Only care when a possible input has been specified
+ if ( !handleObj.data || !handleObj.data.keys || typeof handleObj.data.keys !== "string" ) {
+ return;
+ }
- var origHandler = handleObj.handler,
- keys = handleObj.data.keys.toLowerCase().split(" "),
- textAcceptingInputTypes = ["text", "password", "number", "email", "url", "range", "date", "month", "week", "time", "datetime", "datetime-local", "search", "color", "tel"];
-
- handleObj.handler = function( event ) {
- // Don't fire in text-accepting inputs that we didn't directly bind to
- if ( this !== event.target && (/textarea|select/i.test( event.target.nodeName ) ||
- jQuery.inArray(event.target.type, textAcceptingInputTypes) > -1 ) ) {
- return;
- }
+ var origHandler = handleObj.handler,
+ keys = handleObj.data.keys.toLowerCase().split(" "),
+ textAcceptingInputTypes = ["text", "password", "number", "email", "url", "range", "date", "month", "week", "time", "datetime", "datetime-local", "search", "color", "tel"];
+
+ handleObj.handler = function( event ) {
+ // Don't fire in text-accepting inputs that we didn't directly bind to
+ if ( this !== event.target && (/textarea|select/i.test( event.target.nodeName ) ||
+ jQuery.inArray(event.target.type, textAcceptingInputTypes) > -1 ) ) {
+ return;
+ }
- var special = jQuery.hotkeys.specialKeys[ event.keyCode ],
- // character codes are available only in keypress
- character = (event.type === "keydown" || event.type === "keypress") && String.fromCharCode( event.which ).toLowerCase()
- modif = "", possible = {};
+ var special = jQuery.hotkeys.specialKeys[ event.keyCode ],
+ // character codes are available only in keypress
+ character = (event.type === "keydown" || event.type === "keypress") && String.fromCharCode( event.which ).toLowerCase()
+ modif = "", possible = {};
- // check combinations (alt|ctrl|shift+anything)
- if ( event.altKey && special !== "alt" ) {
- modif += "alt+";
- }
+ // check combinations (alt|ctrl|shift+anything)
+ if ( event.altKey && special !== "alt" ) {
+ modif += "alt+";
+ }
- if ( event.ctrlKey && special !== "ctrl" ) {
- modif += "ctrl+";
- }
-
- // TODO: Need to make sure this works consistently across platforms
- if ( event.metaKey && !event.ctrlKey && special !== "meta" ) {
- modif += "meta+";
- }
+ if ( event.ctrlKey && special !== "ctrl" ) {
+ modif += "ctrl+";
+ }
+
+ // TODO: Need to make sure this works consistently across platforms
+ if ( event.metaKey && !event.ctrlKey && special !== "meta" ) {
+ modif += "meta+";
+ }
- if ( event.shiftKey && special !== "shift" ) {
- modif += "shift+";
- }
+ if ( event.shiftKey && special !== "shift" ) {
+ modif += "shift+";
+ }
- if ( special ) {
- possible[ modif + special ] = true;
- }
+ if ( special ) {
+ possible[ modif + special ] = true;
+ }
- if ( character ) {
- possible[ modif + character ] = true;
- possible[ modif + jQuery.hotkeys.shiftNums[ character ] ] = true;
+ if ( character ) {
+ possible[ modif + character ] = true;
+ possible[ modif + jQuery.hotkeys.shiftNums[ character ] ] = true;
- // "$" can be triggered as "Shift+4" or "Shift+$" or just "$"
- if ( modif === "shift+" ) {
- possible[ jQuery.hotkeys.shiftNums[ character ] ] = true;
- }
- }
+ // "$" can be triggered as "Shift+4" or "Shift+$" or just "$"
+ if ( modif === "shift+" ) {
+ possible[ jQuery.hotkeys.shiftNums[ character ] ] = true;
+ }
+ }
- for ( var i = 0, l = keys.length; i < l; i++ ) {
- if ( possible[ keys[i] ] ) {
- return origHandler.apply( this, arguments );
- }
- }
- };
- }
+ for ( var i = 0, l = keys.length; i < l; i++ ) {
+ if ( possible[ keys[i] ] ) {
+ return origHandler.apply( this, arguments );
+ }
+ }
+ };
+ }
- jQuery.each([ "keydown", "keyup", "keypress" ], function() {
- jQuery.event.special[ this ] = { add: keyHandler };
- });
+ jQuery.each([ "keydown", "keyup", "keypress" ], function() {
+ jQuery.event.special[ this ] = { add: keyHandler };
+ });
})( this.jQuery );
diff --git a/static/js/lib/jquery.js b/static/js/lib/jquery.js
index f650a46..5c54dff 100644
--- a/static/js/lib/jquery.js
+++ b/static/js/lib/jquery.js
@@ -19,847 +19,847 @@
// Support: Firefox 18+
//"use strict";
var
- // A central reference to the root jQuery(document)
- rootjQuery,
+ // A central reference to the root jQuery(document)
+ rootjQuery,
- // The deferred used on DOM ready
- readyList,
+ // The deferred used on DOM ready
+ readyList,
- // Support: IE9
- // For `typeof xmlNode.method` instead of `xmlNode.method !== undefined`
- core_strundefined = typeof undefined,
+ // Support: IE9
+ // For `typeof xmlNode.method` instead of `xmlNode.method !== undefined`
+ core_strundefined = typeof undefined,
- // Use the correct document accordingly with window argument (sandbox)
- location = window.location,
- document = window.document,
- docElem = document.documentElement,
+ // Use the correct document accordingly with window argument (sandbox)
+ location = window.location,
+ document = window.document,
+ docElem = document.documentElement,
- // Map over jQuery in case of overwrite
- _jQuery = window.jQuery,
+ // Map over jQuery in case of overwrite
+ _jQuery = window.jQuery,
- // Map over the $ in case of overwrite
- _$ = window.$,
+ // Map over the $ in case of overwrite
+ _$ = window.$,
- // [[Class]] -> type pairs
- class2type = {},
+ // [[Class]] -> type pairs
+ class2type = {},
- // List of deleted data cache ids, so we can reuse them
- core_deletedIds = [],
+ // List of deleted data cache ids, so we can reuse them
+ core_deletedIds = [],
- core_version = "2.0.2",
+ core_version = "2.0.2",
- // Save a reference to some core methods
- core_concat = core_deletedIds.concat,
- core_push = core_deletedIds.push,
- core_slice = core_deletedIds.slice,
- core_indexOf = core_deletedIds.indexOf,
- core_toString = class2type.toString,
- core_hasOwn = class2type.hasOwnProperty,
- core_trim = core_version.trim,
+ // Save a reference to some core methods
+ core_concat = core_deletedIds.concat,
+ core_push = core_deletedIds.push,
+ core_slice = core_deletedIds.slice,
+ core_indexOf = core_deletedIds.indexOf,
+ core_toString = class2type.toString,
+ core_hasOwn = class2type.hasOwnProperty,
+ core_trim = core_version.trim,
- // Define a local copy of jQuery
- jQuery = function( selector, context ) {
- // The jQuery object is actually just the init constructor 'enhanced'
- return new jQuery.fn.init( selector, context, rootjQuery );
- },
+ // Define a local copy of jQuery
+ jQuery = function( selector, context ) {
+ // The jQuery object is actually just the init constructor 'enhanced'
+ return new jQuery.fn.init( selector, context, rootjQuery );
+ },
- // Used for matching numbers
- core_pnum = /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,
+ // Used for matching numbers
+ core_pnum = /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,
- // Used for splitting on whitespace
- core_rnotwhite = /\S+/g,
+ // Used for splitting on whitespace
+ core_rnotwhite = /\S+/g,
- // A simple way to check for HTML strings
- // Prioritize #id over
to avoid XSS via location.hash (#9521)
- // Strict HTML recognition (#11290: must start with <)
- rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,
+ // A simple way to check for HTML strings
+ // Prioritize #id over to avoid XSS via location.hash (#9521)
+ // Strict HTML recognition (#11290: must start with <)
+ rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,
- // Match a standalone tag
- rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>|)$/,
+ // Match a standalone tag
+ rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>|)$/,
- // Matches dashed string for camelizing
- rmsPrefix = /^-ms-/,
- rdashAlpha = /-([\da-z])/gi,
+ // Matches dashed string for camelizing
+ rmsPrefix = /^-ms-/,
+ rdashAlpha = /-([\da-z])/gi,
- // Used by jQuery.camelCase as callback to replace()
- fcamelCase = function( all, letter ) {
- return letter.toUpperCase();
- },
+ // Used by jQuery.camelCase as callback to replace()
+ fcamelCase = function( all, letter ) {
+ return letter.toUpperCase();
+ },
- // The ready event handler and self cleanup method
- completed = function() {
- document.removeEventListener( "DOMContentLoaded", completed, false );
- window.removeEventListener( "load", completed, false );
- jQuery.ready();
- };
+ // The ready event handler and self cleanup method
+ completed = function() {
+ document.removeEventListener( "DOMContentLoaded", completed, false );
+ window.removeEventListener( "load", completed, false );
+ jQuery.ready();
+ };
jQuery.fn = jQuery.prototype = {
- // The current version of jQuery being used
- jquery: core_version,
+ // The current version of jQuery being used
+ jquery: core_version,
- constructor: jQuery,
- init: function( selector, context, rootjQuery ) {
- var match, elem;
+ constructor: jQuery,
+ init: function( selector, context, rootjQuery ) {
+ var match, elem;
- // HANDLE: $(""), $(null), $(undefined), $(false)
- if ( !selector ) {
- return this;
- }
+ // HANDLE: $(""), $(null), $(undefined), $(false)
+ if ( !selector ) {
+ return this;
+ }
- // Handle HTML strings
- if ( typeof selector === "string" ) {
- if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {
- // Assume that strings that start and end with <> are HTML and skip the regex check
- match = [ null, selector, null ];
+ // Handle HTML strings
+ if ( typeof selector === "string" ) {
+ if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {
+ // Assume that strings that start and end with <> are HTML and skip the regex check
+ match = [ null, selector, null ];
- } else {
- match = rquickExpr.exec( selector );
- }
+ } else {
+ match = rquickExpr.exec( selector );
+ }
- // Match html or make sure no context is specified for #id
- if ( match && (match[1] || !context) ) {
+ // Match html or make sure no context is specified for #id
+ if ( match && (match[1] || !context) ) {
- // HANDLE: $(html) -> $(array)
- if ( match[1] ) {
- context = context instanceof jQuery ? context[0] : context;
+ // HANDLE: $(html) -> $(array)
+ if ( match[1] ) {
+ context = context instanceof jQuery ? context[0] : context;
- // scripts is true for back-compat
- jQuery.merge( this, jQuery.parseHTML(
- match[1],
- context && context.nodeType ? context.ownerDocument || context : document,
- true
- ) );
+ // scripts is true for back-compat
+ jQuery.merge( this, jQuery.parseHTML(
+ match[1],
+ context && context.nodeType ? context.ownerDocument || context : document,
+ true
+ ) );
- // HANDLE: $(html, props)
- if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {
- for ( match in context ) {
- // Properties of context are called as methods if possible
- if ( jQuery.isFunction( this[ match ] ) ) {
- this[ match ]( context[ match ] );
+ // HANDLE: $(html, props)
+ if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {
+ for ( match in context ) {
+ // Properties of context are called as methods if possible
+ if ( jQuery.isFunction( this[ match ] ) ) {
+ this[ match ]( context[ match ] );
- // ...and otherwise set as attributes
- } else {
- this.attr( match, context[ match ] );
- }
- }
- }
+ // ...and otherwise set as attributes
+ } else {
+ this.attr( match, context[ match ] );
+ }
+ }
+ }
- return this;
+ return this;
- // HANDLE: $(#id)
- } else {
- elem = document.getElementById( match[2] );
+ // HANDLE: $(#id)
+ } else {
+ elem = document.getElementById( match[2] );
- // Check parentNode to catch when Blackberry 4.6 returns
- // nodes that are no longer in the document #6963
- if ( elem && elem.parentNode ) {
- // Inject the element directly into the jQuery object
- this.length = 1;
- this[0] = elem;
- }
+ // Check parentNode to catch when Blackberry 4.6 returns
+ // nodes that are no longer in the document #6963
+ if ( elem && elem.parentNode ) {
+ // Inject the element directly into the jQuery object
+ this.length = 1;
+ this[0] = elem;
+ }
- this.context = document;
- this.selector = selector;
- return this;
- }
+ this.context = document;
+ this.selector = selector;
+ return this;
+ }
- // HANDLE: $(expr, $(...))
- } else if ( !context || context.jquery ) {
- return ( context || rootjQuery ).find( selector );
+ // HANDLE: $(expr, $(...))
+ } else if ( !context || context.jquery ) {
+ return ( context || rootjQuery ).find( selector );
- // HANDLE: $(expr, context)
- // (which is just equivalent to: $(context).find(expr)
- } else {
- return this.constructor( context ).find( selector );
- }
+ // HANDLE: $(expr, context)
+ // (which is just equivalent to: $(context).find(expr)
+ } else {
+ return this.constructor( context ).find( selector );
+ }
- // HANDLE: $(DOMElement)
- } else if ( selector.nodeType ) {
- this.context = this[0] = selector;
- this.length = 1;
- return this;
+ // HANDLE: $(DOMElement)
+ } else if ( selector.nodeType ) {
+ this.context = this[0] = selector;
+ this.length = 1;
+ return this;
- // HANDLE: $(function)
- // Shortcut for document ready
- } else if ( jQuery.isFunction( selector ) ) {
- return rootjQuery.ready( selector );
- }
+ // HANDLE: $(function)
+ // Shortcut for document ready
+ } else if ( jQuery.isFunction( selector ) ) {
+ return rootjQuery.ready( selector );
+ }
- if ( selector.selector !== undefined ) {
- this.selector = selector.selector;
- this.context = selector.context;
- }
+ if ( selector.selector !== undefined ) {
+ this.selector = selector.selector;
+ this.context = selector.context;
+ }
- return jQuery.makeArray( selector, this );
- },
+ return jQuery.makeArray( selector, this );
+ },
- // Start with an empty selector
- selector: "",
+ // Start with an empty selector
+ selector: "",
- // The default length of a jQuery object is 0
- length: 0,
+ // The default length of a jQuery object is 0
+ length: 0,
- toArray: function() {
- return core_slice.call( this );
- },
+ toArray: function() {
+ return core_slice.call( this );
+ },
- // Get the Nth element in the matched element set OR
- // Get the whole matched element set as a clean array
- get: function( num ) {
- return num == null ?
+ // Get the Nth element in the matched element set OR
+ // Get the whole matched element set as a clean array
+ get: function( num ) {
+ return num == null ?
- // Return a 'clean' array
- this.toArray() :
+ // Return a 'clean' array
+ this.toArray() :
- // Return just the object
- ( num < 0 ? this[ this.length + num ] : this[ num ] );
- },
+ // Return just the object
+ ( num < 0 ? this[ this.length + num ] : this[ num ] );
+ },
- // Take an array of elements and push it onto the stack
- // (returning the new matched element set)
- pushStack: function( elems ) {
+ // Take an array of elements and push it onto the stack
+ // (returning the new matched element set)
+ pushStack: function( elems ) {
- // Build a new jQuery matched element set
- var ret = jQuery.merge( this.constructor(), elems );
+ // Build a new jQuery matched element set
+ var ret = jQuery.merge( this.constructor(), elems );
- // Add the old object onto the stack (as a reference)
- ret.prevObject = this;
- ret.context = this.context;
+ // Add the old object onto the stack (as a reference)
+ ret.prevObject = this;
+ ret.context = this.context;
- // Return the newly-formed element set
- return ret;
- },
+ // Return the newly-formed element set
+ return ret;
+ },
- // Execute a callback for every element in the matched set.
- // (You can seed the arguments with an array of args, but this is
- // only used internally.)
- each: function( callback, args ) {
- return jQuery.each( this, callback, args );
- },
+ // Execute a callback for every element in the matched set.
+ // (You can seed the arguments with an array of args, but this is
+ // only used internally.)
+ each: function( callback, args ) {
+ return jQuery.each( this, callback, args );
+ },
- ready: function( fn ) {
- // Add the callback
- jQuery.ready.promise().done( fn );
+ ready: function( fn ) {
+ // Add the callback
+ jQuery.ready.promise().done( fn );
- return this;
- },
+ return this;
+ },
- slice: function() {
- return this.pushStack( core_slice.apply( this, arguments ) );
- },
+ slice: function() {
+ return this.pushStack( core_slice.apply( this, arguments ) );
+ },
- first: function() {
- return this.eq( 0 );
- },
+ first: function() {
+ return this.eq( 0 );
+ },
- last: function() {
- return this.eq( -1 );
- },
+ last: function() {
+ return this.eq( -1 );
+ },
- eq: function( i ) {
- var len = this.length,
- j = +i + ( i < 0 ? len : 0 );
- return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] );
- },
+ eq: function( i ) {
+ var len = this.length,
+ j = +i + ( i < 0 ? len : 0 );
+ return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] );
+ },
- map: function( callback ) {
- return this.pushStack( jQuery.map(this, function( elem, i ) {
- return callback.call( elem, i, elem );
- }));
- },
+ map: function( callback ) {
+ return this.pushStack( jQuery.map(this, function( elem, i ) {
+ return callback.call( elem, i, elem );
+ }));
+ },
- end: function() {
- return this.prevObject || this.constructor(null);
- },
+ end: function() {
+ return this.prevObject || this.constructor(null);
+ },
- // For internal use only.
- // Behaves like an Array's method, not like a jQuery method.
- push: core_push,
- sort: [].sort,
- splice: [].splice
+ // For internal use only.
+ // Behaves like an Array's method, not like a jQuery method.
+ push: core_push,
+ sort: [].sort,
+ splice: [].splice
};
// Give the init function the jQuery prototype for later instantiation
jQuery.fn.init.prototype = jQuery.fn;
jQuery.extend = jQuery.fn.extend = function() {
- var options, name, src, copy, copyIsArray, clone,
- target = arguments[0] || {},
- i = 1,
- length = arguments.length,
- deep = false;
+ var options, name, src, copy, copyIsArray, clone,
+ target = arguments[0] || {},
+ i = 1,
+ length = arguments.length,
+ deep = false;
- // Handle a deep copy situation
- if ( typeof target === "boolean" ) {
- deep = target;
- target = arguments[1] || {};
- // skip the boolean and the target
- i = 2;
- }
+ // Handle a deep copy situation
+ if ( typeof target === "boolean" ) {
+ deep = target;
+ target = arguments[1] || {};
+ // skip the boolean and the target
+ i = 2;
+ }
- // Handle case when target is a string or something (possible in deep copy)
- if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
- target = {};
- }
+ // Handle case when target is a string or something (possible in deep copy)
+ if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
+ target = {};
+ }
- // extend jQuery itself if only one argument is passed
- if ( length === i ) {
- target = this;
- --i;
- }
+ // extend jQuery itself if only one argument is passed
+ if ( length === i ) {
+ target = this;
+ --i;
+ }
- for ( ; i < length; i++ ) {
- // Only deal with non-null/undefined values
- if ( (options = arguments[ i ]) != null ) {
- // Extend the base object
- for ( name in options ) {
- src = target[ name ];
- copy = options[ name ];
+ for ( ; i < length; i++ ) {
+ // Only deal with non-null/undefined values
+ if ( (options = arguments[ i ]) != null ) {
+ // Extend the base object
+ for ( name in options ) {
+ src = target[ name ];
+ copy = options[ name ];
- // Prevent never-ending loop
- if ( target === copy ) {
- continue;
- }
+ // Prevent never-ending loop
+ if ( target === copy ) {
+ continue;
+ }
- // Recurse if we're merging plain objects or arrays
- if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
- if ( copyIsArray ) {
- copyIsArray = false;
- clone = src && jQuery.isArray(src) ? src : [];
+ // Recurse if we're merging plain objects or arrays
+ if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
+ if ( copyIsArray ) {
+ copyIsArray = false;
+ clone = src && jQuery.isArray(src) ? src : [];
- } else {
- clone = src && jQuery.isPlainObject(src) ? src : {};
- }
+ } else {
+ clone = src && jQuery.isPlainObject(src) ? src : {};
+ }
- // Never move original objects, clone them
- target[ name ] = jQuery.extend( deep, clone, copy );
+ // Never move original objects, clone them
+ target[ name ] = jQuery.extend( deep, clone, copy );
- // Don't bring in undefined values
- } else if ( copy !== undefined ) {
- target[ name ] = copy;
- }
- }
- }
- }
+ // Don't bring in undefined values
+ } else if ( copy !== undefined ) {
+ target[ name ] = copy;
+ }
+ }
+ }
+ }
- // Return the modified object
- return target;
+ // Return the modified object
+ return target;
};
jQuery.extend({
- // Unique for each copy of jQuery on the page
- expando: "jQuery" + ( core_version + Math.random() ).replace( /\D/g, "" ),
-
- noConflict: function( deep ) {
- if ( window.$ === jQuery ) {
- window.$ = _$;
- }
-
- if ( deep && window.jQuery === jQuery ) {
- window.jQuery = _jQuery;
- }
-
- return jQuery;
- },
-
- // Is the DOM ready to be used? Set to true once it occurs.
- isReady: false,
-
- // A counter to track how many items to wait for before
- // the ready event fires. See #6781
- readyWait: 1,
-
- // Hold (or release) the ready event
- holdReady: function( hold ) {
- if ( hold ) {
- jQuery.readyWait++;
- } else {
- jQuery.ready( true );
- }
- },
-
- // Handle when the DOM is ready
- ready: function( wait ) {
-
- // Abort if there are pending holds or we're already ready
- if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
- return;
- }
-
- // Remember that the DOM is ready
- jQuery.isReady = true;
-
- // If a normal DOM Ready event fired, decrement, and wait if need be
- if ( wait !== true && --jQuery.readyWait > 0 ) {
- return;
- }
-
- // If there are functions bound, to execute
- readyList.resolveWith( document, [ jQuery ] );
-
- // Trigger any bound ready events
- if ( jQuery.fn.trigger ) {
- jQuery( document ).trigger("ready").off("ready");
- }
- },
-
- // See test/unit/core.js for details concerning isFunction.
- // Since version 1.3, DOM methods and functions like alert
- // aren't supported. They return false on IE (#2968).
- isFunction: function( obj ) {
- return jQuery.type(obj) === "function";
- },
-
- isArray: Array.isArray,
-
- isWindow: function( obj ) {
- return obj != null && obj === obj.window;
- },
-
- isNumeric: function( obj ) {
- return !isNaN( parseFloat(obj) ) && isFinite( obj );
- },
-
- type: function( obj ) {
- if ( obj == null ) {
- return String( obj );
- }
- // Support: Safari <= 5.1 (functionish RegExp)
- return typeof obj === "object" || typeof obj === "function" ?
- class2type[ core_toString.call(obj) ] || "object" :
- typeof obj;
- },
-
- isPlainObject: function( obj ) {
- // Not plain objects:
- // - Any object or value whose internal [[Class]] property is not "[object Object]"
- // - DOM nodes
- // - window
- if ( jQuery.type( obj ) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
- return false;
- }
-
- // Support: Firefox <20
- // The try/catch suppresses exceptions thrown when attempting to access
- // the "constructor" property of certain host objects, ie. |window.location|
- // https://bugzilla.mozilla.org/show_bug.cgi?id=814622
- try {
- if ( obj.constructor &&
- !core_hasOwn.call( obj.constructor.prototype, "isPrototypeOf" ) ) {
- return false;
- }
- } catch ( e ) {
- return false;
- }
-
- // If the function hasn't returned already, we're confident that
- // |obj| is a plain object, created by {} or constructed with new Object
- return true;
- },
-
- isEmptyObject: function( obj ) {
- var name;
- for ( name in obj ) {
- return false;
- }
- return true;
- },
-
- error: function( msg ) {
- throw new Error( msg );
- },
-
- // data: string of html
- // context (optional): If specified, the fragment will be created in this context, defaults to document
- // keepScripts (optional): If true, will include scripts passed in the html string
- parseHTML: function( data, context, keepScripts ) {
- if ( !data || typeof data !== "string" ) {
- return null;
- }
- if ( typeof context === "boolean" ) {
- keepScripts = context;
- context = false;
- }
- context = context || document;
-
- var parsed = rsingleTag.exec( data ),
- scripts = !keepScripts && [];
-
- // Single tag
- if ( parsed ) {
- return [ context.createElement( parsed[1] ) ];
- }
-
- parsed = jQuery.buildFragment( [ data ], context, scripts );
-
- if ( scripts ) {
- jQuery( scripts ).remove();
- }
-
- return jQuery.merge( [], parsed.childNodes );
- },
-
- parseJSON: JSON.parse,
-
- // Cross-browser xml parsing
- parseXML: function( data ) {
- var xml, tmp;
- if ( !data || typeof data !== "string" ) {
- return null;
- }
-
- // Support: IE9
- try {
- tmp = new DOMParser();
- xml = tmp.parseFromString( data , "text/xml" );
- } catch ( e ) {
- xml = undefined;
- }
-
- if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) {
- jQuery.error( "Invalid XML: " + data );
- }
- return xml;
- },
-
- noop: function() {},
-
- // Evaluates a script in a global context
- globalEval: function( code ) {
- var script,
- indirect = eval;
-
- code = jQuery.trim( code );
-
- if ( code ) {
- // If the code includes a valid, prologue position
- // strict mode pragma, execute code by injecting a
- // script tag into the document.
- if ( code.indexOf("use strict") === 1 ) {
- script = document.createElement("script");
- script.text = code;
- document.head.appendChild( script ).parentNode.removeChild( script );
- } else {
- // Otherwise, avoid the DOM node creation, insertion
- // and removal by using an indirect global eval
- indirect( code );
- }
- }
- },
-
- // Convert dashed to camelCase; used by the css and data modules
- // Microsoft forgot to hump their vendor prefix (#9572)
- camelCase: function( string ) {
- return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
- },
-
- nodeName: function( elem, name ) {
- return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
- },
-
- // args is for internal usage only
- each: function( obj, callback, args ) {
- var value,
- i = 0,
- length = obj.length,
- isArray = isArraylike( obj );
-
- if ( args ) {
- if ( isArray ) {
- for ( ; i < length; i++ ) {
- value = callback.apply( obj[ i ], args );
-
- if ( value === false ) {
- break;
- }
- }
- } else {
- for ( i in obj ) {
- value = callback.apply( obj[ i ], args );
-
- if ( value === false ) {
- break;
- }
- }
- }
-
- // A special, fast, case for the most common use of each
- } else {
- if ( isArray ) {
- for ( ; i < length; i++ ) {
- value = callback.call( obj[ i ], i, obj[ i ] );
-
- if ( value === false ) {
- break;
- }
- }
- } else {
- for ( i in obj ) {
- value = callback.call( obj[ i ], i, obj[ i ] );
-
- if ( value === false ) {
- break;
- }
- }
- }
- }
-
- return obj;
- },
-
- trim: function( text ) {
- return text == null ? "" : core_trim.call( text );
- },
-
- // results is for internal usage only
- makeArray: function( arr, results ) {
- var ret = results || [];
-
- if ( arr != null ) {
- if ( isArraylike( Object(arr) ) ) {
- jQuery.merge( ret,
- typeof arr === "string" ?
- [ arr ] : arr
- );
- } else {
- core_push.call( ret, arr );
- }
- }
-
- return ret;
- },
-
- inArray: function( elem, arr, i ) {
- return arr == null ? -1 : core_indexOf.call( arr, elem, i );
- },
-
- merge: function( first, second ) {
- var l = second.length,
- i = first.length,
- j = 0;
-
- if ( typeof l === "number" ) {
- for ( ; j < l; j++ ) {
- first[ i++ ] = second[ j ];
- }
- } else {
- while ( second[j] !== undefined ) {
- first[ i++ ] = second[ j++ ];
- }
- }
-
- first.length = i;
-
- return first;
- },
-
- grep: function( elems, callback, inv ) {
- var retVal,
- ret = [],
- i = 0,
- length = elems.length;
- inv = !!inv;
-
- // Go through the array, only saving the items
- // that pass the validator function
- for ( ; i < length; i++ ) {
- retVal = !!callback( elems[ i ], i );
- if ( inv !== retVal ) {
- ret.push( elems[ i ] );
- }
- }
-
- return ret;
- },
-
- // arg is for internal usage only
- map: function( elems, callback, arg ) {
- var value,
- i = 0,
- length = elems.length,
- isArray = isArraylike( elems ),
- ret = [];
-
- // Go through the array, translating each of the items to their
- if ( isArray ) {
- for ( ; i < length; i++ ) {
- value = callback( elems[ i ], i, arg );
-
- if ( value != null ) {
- ret[ ret.length ] = value;
- }
- }
-
- // Go through every key on the object,
- } else {
- for ( i in elems ) {
- value = callback( elems[ i ], i, arg );
-
- if ( value != null ) {
- ret[ ret.length ] = value;
- }
- }
- }
-
- // Flatten any nested arrays
- return core_concat.apply( [], ret );
- },
-
- // A global GUID counter for objects
- guid: 1,
-
- // Bind a function to a context, optionally partially applying any
- // arguments.
- proxy: function( fn, context ) {
- var tmp, args, proxy;
-
- if ( typeof context === "string" ) {
- tmp = fn[ context ];
- context = fn;
- fn = tmp;
- }
-
- // Quick check to determine if target is callable, in the spec
- // this throws a TypeError, but we will just return undefined.
- if ( !jQuery.isFunction( fn ) ) {
- return undefined;
- }
-
- // Simulated bind
- args = core_slice.call( arguments, 2 );
- proxy = function() {
- return fn.apply( context || this, args.concat( core_slice.call( arguments ) ) );
- };
-
- // Set the guid of unique handler to the same of original handler, so it can be removed
- proxy.guid = fn.guid = fn.guid || jQuery.guid++;
-
- return proxy;
- },
-
- // Multifunctional method to get and set values of a collection
- // The value/s can optionally be executed if it's a function
- access: function( elems, fn, key, value, chainable, emptyGet, raw ) {
- var i = 0,
- length = elems.length,
- bulk = key == null;
-
- // Sets many values
- if ( jQuery.type( key ) === "object" ) {
- chainable = true;
- for ( i in key ) {
- jQuery.access( elems, fn, i, key[i], true, emptyGet, raw );
- }
-
- // Sets one value
- } else if ( value !== undefined ) {
- chainable = true;
-
- if ( !jQuery.isFunction( value ) ) {
- raw = true;
- }
-
- if ( bulk ) {
- // Bulk operations run against the entire set
- if ( raw ) {
- fn.call( elems, value );
- fn = null;
-
- // ...except when executing function values
- } else {
- bulk = fn;
- fn = function( elem, key, value ) {
- return bulk.call( jQuery( elem ), value );
- };
- }
- }
-
- if ( fn ) {
- for ( ; i < length; i++ ) {
- fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) );
- }
- }
- }
-
- return chainable ?
- elems :
-
- // Gets
- bulk ?
- fn.call( elems ) :
- length ? fn( elems[0], key ) : emptyGet;
- },
-
- now: Date.now,
-
- // A method for quickly swapping in/out CSS properties to get correct calculations.
- // Note: this method belongs to the css module but it's needed here for the support module.
- // If support gets modularized, this method should be moved back to the css module.
- swap: function( elem, options, callback, args ) {
- var ret, name,
- old = {};
-
- // Remember the old values, and insert the new ones
- for ( name in options ) {
- old[ name ] = elem.style[ name ];
- elem.style[ name ] = options[ name ];
- }
-
- ret = callback.apply( elem, args || [] );
-
- // Revert the old values
- for ( name in options ) {
- elem.style[ name ] = old[ name ];
- }
-
- return ret;
- }
+ // Unique for each copy of jQuery on the page
+ expando: "jQuery" + ( core_version + Math.random() ).replace( /\D/g, "" ),
+
+ noConflict: function( deep ) {
+ if ( window.$ === jQuery ) {
+ window.$ = _$;
+ }
+
+ if ( deep && window.jQuery === jQuery ) {
+ window.jQuery = _jQuery;
+ }
+
+ return jQuery;
+ },
+
+ // Is the DOM ready to be used? Set to true once it occurs.
+ isReady: false,
+
+ // A counter to track how many items to wait for before
+ // the ready event fires. See #6781
+ readyWait: 1,
+
+ // Hold (or release) the ready event
+ holdReady: function( hold ) {
+ if ( hold ) {
+ jQuery.readyWait++;
+ } else {
+ jQuery.ready( true );
+ }
+ },
+
+ // Handle when the DOM is ready
+ ready: function( wait ) {
+
+ // Abort if there are pending holds or we're already ready
+ if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
+ return;
+ }
+
+ // Remember that the DOM is ready
+ jQuery.isReady = true;
+
+ // If a normal DOM Ready event fired, decrement, and wait if need be
+ if ( wait !== true && --jQuery.readyWait > 0 ) {
+ return;
+ }
+
+ // If there are functions bound, to execute
+ readyList.resolveWith( document, [ jQuery ] );
+
+ // Trigger any bound ready events
+ if ( jQuery.fn.trigger ) {
+ jQuery( document ).trigger("ready").off("ready");
+ }
+ },
+
+ // See test/unit/core.js for details concerning isFunction.
+ // Since version 1.3, DOM methods and functions like alert
+ // aren't supported. They return false on IE (#2968).
+ isFunction: function( obj ) {
+ return jQuery.type(obj) === "function";
+ },
+
+ isArray: Array.isArray,
+
+ isWindow: function( obj ) {
+ return obj != null && obj === obj.window;
+ },
+
+ isNumeric: function( obj ) {
+ return !isNaN( parseFloat(obj) ) && isFinite( obj );
+ },
+
+ type: function( obj ) {
+ if ( obj == null ) {
+ return String( obj );
+ }
+ // Support: Safari <= 5.1 (functionish RegExp)
+ return typeof obj === "object" || typeof obj === "function" ?
+ class2type[ core_toString.call(obj) ] || "object" :
+ typeof obj;
+ },
+
+ isPlainObject: function( obj ) {
+ // Not plain objects:
+ // - Any object or value whose internal [[Class]] property is not "[object Object]"
+ // - DOM nodes
+ // - window
+ if ( jQuery.type( obj ) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
+ return false;
+ }
+
+ // Support: Firefox <20
+ // The try/catch suppresses exceptions thrown when attempting to access
+ // the "constructor" property of certain host objects, ie. |window.location|
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=814622
+ try {
+ if ( obj.constructor &&
+ !core_hasOwn.call( obj.constructor.prototype, "isPrototypeOf" ) ) {
+ return false;
+ }
+ } catch ( e ) {
+ return false;
+ }
+
+ // If the function hasn't returned already, we're confident that
+ // |obj| is a plain object, created by {} or constructed with new Object
+ return true;
+ },
+
+ isEmptyObject: function( obj ) {
+ var name;
+ for ( name in obj ) {
+ return false;
+ }
+ return true;
+ },
+
+ error: function( msg ) {
+ throw new Error( msg );
+ },
+
+ // data: string of html
+ // context (optional): If specified, the fragment will be created in this context, defaults to document
+ // keepScripts (optional): If true, will include scripts passed in the html string
+ parseHTML: function( data, context, keepScripts ) {
+ if ( !data || typeof data !== "string" ) {
+ return null;
+ }
+ if ( typeof context === "boolean" ) {
+ keepScripts = context;
+ context = false;
+ }
+ context = context || document;
+
+ var parsed = rsingleTag.exec( data ),
+ scripts = !keepScripts && [];
+
+ // Single tag
+ if ( parsed ) {
+ return [ context.createElement( parsed[1] ) ];
+ }
+
+ parsed = jQuery.buildFragment( [ data ], context, scripts );
+
+ if ( scripts ) {
+ jQuery( scripts ).remove();
+ }
+
+ return jQuery.merge( [], parsed.childNodes );
+ },
+
+ parseJSON: JSON.parse,
+
+ // Cross-browser xml parsing
+ parseXML: function( data ) {
+ var xml, tmp;
+ if ( !data || typeof data !== "string" ) {
+ return null;
+ }
+
+ // Support: IE9
+ try {
+ tmp = new DOMParser();
+ xml = tmp.parseFromString( data , "text/xml" );
+ } catch ( e ) {
+ xml = undefined;
+ }
+
+ if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) {
+ jQuery.error( "Invalid XML: " + data );
+ }
+ return xml;
+ },
+
+ noop: function() {},
+
+ // Evaluates a script in a global context
+ globalEval: function( code ) {
+ var script,
+ indirect = eval;
+
+ code = jQuery.trim( code );
+
+ if ( code ) {
+ // If the code includes a valid, prologue position
+ // strict mode pragma, execute code by injecting a
+ // script tag into the document.
+ if ( code.indexOf("use strict") === 1 ) {
+ script = document.createElement("script");
+ script.text = code;
+ document.head.appendChild( script ).parentNode.removeChild( script );
+ } else {
+ // Otherwise, avoid the DOM node creation, insertion
+ // and removal by using an indirect global eval
+ indirect( code );
+ }
+ }
+ },
+
+ // Convert dashed to camelCase; used by the css and data modules
+ // Microsoft forgot to hump their vendor prefix (#9572)
+ camelCase: function( string ) {
+ return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
+ },
+
+ nodeName: function( elem, name ) {
+ return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
+ },
+
+ // args is for internal usage only
+ each: function( obj, callback, args ) {
+ var value,
+ i = 0,
+ length = obj.length,
+ isArray = isArraylike( obj );
+
+ if ( args ) {
+ if ( isArray ) {
+ for ( ; i < length; i++ ) {
+ value = callback.apply( obj[ i ], args );
+
+ if ( value === false ) {
+ break;
+ }
+ }
+ } else {
+ for ( i in obj ) {
+ value = callback.apply( obj[ i ], args );
+
+ if ( value === false ) {
+ break;
+ }
+ }
+ }
+
+ // A special, fast, case for the most common use of each
+ } else {
+ if ( isArray ) {
+ for ( ; i < length; i++ ) {
+ value = callback.call( obj[ i ], i, obj[ i ] );
+
+ if ( value === false ) {
+ break;
+ }
+ }
+ } else {
+ for ( i in obj ) {
+ value = callback.call( obj[ i ], i, obj[ i ] );
+
+ if ( value === false ) {
+ break;
+ }
+ }
+ }
+ }
+
+ return obj;
+ },
+
+ trim: function( text ) {
+ return text == null ? "" : core_trim.call( text );
+ },
+
+ // results is for internal usage only
+ makeArray: function( arr, results ) {
+ var ret = results || [];
+
+ if ( arr != null ) {
+ if ( isArraylike( Object(arr) ) ) {
+ jQuery.merge( ret,
+ typeof arr === "string" ?
+ [ arr ] : arr
+ );
+ } else {
+ core_push.call( ret, arr );
+ }
+ }
+
+ return ret;
+ },
+
+ inArray: function( elem, arr, i ) {
+ return arr == null ? -1 : core_indexOf.call( arr, elem, i );
+ },
+
+ merge: function( first, second ) {
+ var l = second.length,
+ i = first.length,
+ j = 0;
+
+ if ( typeof l === "number" ) {
+ for ( ; j < l; j++ ) {
+ first[ i++ ] = second[ j ];
+ }
+ } else {
+ while ( second[j] !== undefined ) {
+ first[ i++ ] = second[ j++ ];
+ }
+ }
+
+ first.length = i;
+
+ return first;
+ },
+
+ grep: function( elems, callback, inv ) {
+ var retVal,
+ ret = [],
+ i = 0,
+ length = elems.length;
+ inv = !!inv;
+
+ // Go through the array, only saving the items
+ // that pass the validator function
+ for ( ; i < length; i++ ) {
+ retVal = !!callback( elems[ i ], i );
+ if ( inv !== retVal ) {
+ ret.push( elems[ i ] );
+ }
+ }
+
+ return ret;
+ },
+
+ // arg is for internal usage only
+ map: function( elems, callback, arg ) {
+ var value,
+ i = 0,
+ length = elems.length,
+ isArray = isArraylike( elems ),
+ ret = [];
+
+ // Go through the array, translating each of the items to their
+ if ( isArray ) {
+ for ( ; i < length; i++ ) {
+ value = callback( elems[ i ], i, arg );
+
+ if ( value != null ) {
+ ret[ ret.length ] = value;
+ }
+ }
+
+ // Go through every key on the object,
+ } else {
+ for ( i in elems ) {
+ value = callback( elems[ i ], i, arg );
+
+ if ( value != null ) {
+ ret[ ret.length ] = value;
+ }
+ }
+ }
+
+ // Flatten any nested arrays
+ return core_concat.apply( [], ret );
+ },
+
+ // A global GUID counter for objects
+ guid: 1,
+
+ // Bind a function to a context, optionally partially applying any
+ // arguments.
+ proxy: function( fn, context ) {
+ var tmp, args, proxy;
+
+ if ( typeof context === "string" ) {
+ tmp = fn[ context ];
+ context = fn;
+ fn = tmp;
+ }
+
+ // Quick check to determine if target is callable, in the spec
+ // this throws a TypeError, but we will just return undefined.
+ if ( !jQuery.isFunction( fn ) ) {
+ return undefined;
+ }
+
+ // Simulated bind
+ args = core_slice.call( arguments, 2 );
+ proxy = function() {
+ return fn.apply( context || this, args.concat( core_slice.call( arguments ) ) );
+ };
+
+ // Set the guid of unique handler to the same of original handler, so it can be removed
+ proxy.guid = fn.guid = fn.guid || jQuery.guid++;
+
+ return proxy;
+ },
+
+ // Multifunctional method to get and set values of a collection
+ // The value/s can optionally be executed if it's a function
+ access: function( elems, fn, key, value, chainable, emptyGet, raw ) {
+ var i = 0,
+ length = elems.length,
+ bulk = key == null;
+
+ // Sets many values
+ if ( jQuery.type( key ) === "object" ) {
+ chainable = true;
+ for ( i in key ) {
+ jQuery.access( elems, fn, i, key[i], true, emptyGet, raw );
+ }
+
+ // Sets one value
+ } else if ( value !== undefined ) {
+ chainable = true;
+
+ if ( !jQuery.isFunction( value ) ) {
+ raw = true;
+ }
+
+ if ( bulk ) {
+ // Bulk operations run against the entire set
+ if ( raw ) {
+ fn.call( elems, value );
+ fn = null;
+
+ // ...except when executing function values
+ } else {
+ bulk = fn;
+ fn = function( elem, key, value ) {
+ return bulk.call( jQuery( elem ), value );
+ };
+ }
+ }
+
+ if ( fn ) {
+ for ( ; i < length; i++ ) {
+ fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) );
+ }
+ }
+ }
+
+ return chainable ?
+ elems :
+
+ // Gets
+ bulk ?
+ fn.call( elems ) :
+ length ? fn( elems[0], key ) : emptyGet;
+ },
+
+ now: Date.now,
+
+ // A method for quickly swapping in/out CSS properties to get correct calculations.
+ // Note: this method belongs to the css module but it's needed here for the support module.
+ // If support gets modularized, this method should be moved back to the css module.
+ swap: function( elem, options, callback, args ) {
+ var ret, name,
+ old = {};
+
+ // Remember the old values, and insert the new ones
+ for ( name in options ) {
+ old[ name ] = elem.style[ name ];
+ elem.style[ name ] = options[ name ];
+ }
+
+ ret = callback.apply( elem, args || [] );
+
+ // Revert the old values
+ for ( name in options ) {
+ elem.style[ name ] = old[ name ];
+ }
+
+ return ret;
+ }
});
jQuery.ready.promise = function( obj ) {
- if ( !readyList ) {
+ if ( !readyList ) {
- readyList = jQuery.Deferred();
+ readyList = jQuery.Deferred();
- // Catch cases where $(document).ready() is called after the browser event has already occurred.
- // we once tried to use readyState "interactive" here, but it caused issues like the one
- // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15
- if ( document.readyState === "complete" ) {
- // Handle it asynchronously to allow scripts the opportunity to delay ready
- setTimeout( jQuery.ready );
+ // Catch cases where $(document).ready() is called after the browser event has already occurred.
+ // we once tried to use readyState "interactive" here, but it caused issues like the one
+ // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15
+ if ( document.readyState === "complete" ) {
+ // Handle it asynchronously to allow scripts the opportunity to delay ready
+ setTimeout( jQuery.ready );
- } else {
+ } else {
- // Use the handy event callback
- document.addEventListener( "DOMContentLoaded", completed, false );
+ // Use the handy event callback
+ document.addEventListener( "DOMContentLoaded", completed, false );
- // A fallback to window.onload, that will always work
- window.addEventListener( "load", completed, false );
- }
- }
- return readyList.promise( obj );
+ // A fallback to window.onload, that will always work
+ window.addEventListener( "load", completed, false );
+ }
+ }
+ return readyList.promise( obj );
};
// Populate the class2type map
jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) {
- class2type[ "[object " + name + "]" ] = name.toLowerCase();
+ class2type[ "[object " + name + "]" ] = name.toLowerCase();
});
function isArraylike( obj ) {
- var length = obj.length,
- type = jQuery.type( obj );
+ var length = obj.length,
+ type = jQuery.type( obj );
- if ( jQuery.isWindow( obj ) ) {
- return false;
- }
+ if ( jQuery.isWindow( obj ) ) {
+ return false;
+ }
- if ( obj.nodeType === 1 && length ) {
- return true;
- }
+ if ( obj.nodeType === 1 && length ) {
+ return true;
+ }
- return type === "array" || type !== "function" &&
- ( length === 0 ||
- typeof length === "number" && length > 0 && ( length - 1 ) in obj );
+ return type === "array" || type !== "function" &&
+ ( length === 0 ||
+ typeof length === "number" && length > 0 && ( length - 1 ) in obj );
}
// All jQuery objects should point back to these
@@ -877,275 +877,275 @@ rootjQuery = jQuery(document);
(function( window, undefined ) {
var i,
- support,
- cachedruns,
- Expr,
- getText,
- isXML,
- compile,
- outermostContext,
- sortInput,
+ support,
+ cachedruns,
+ Expr,
+ getText,
+ isXML,
+ compile,
+ outermostContext,
+ sortInput,
- // Local document vars
- setDocument,
- document,
- docElem,
- documentIsHTML,
- rbuggyQSA,
- rbuggyMatches,
- matches,
- contains,
+ // Local document vars
+ setDocument,
+ document,
+ docElem,
+ documentIsHTML,
+ rbuggyQSA,
+ rbuggyMatches,
+ matches,
+ contains,
- // Instance-specific data
- expando = "sizzle" + -(new Date()),
- preferredDoc = window.document,
- dirruns = 0,
- done = 0,
- classCache = createCache(),
- tokenCache = createCache(),
- compilerCache = createCache(),
- hasDuplicate = false,
- sortOrder = function() { return 0; },
+ // Instance-specific data
+ expando = "sizzle" + -(new Date()),
+ preferredDoc = window.document,
+ dirruns = 0,
+ done = 0,
+ classCache = createCache(),
+ tokenCache = createCache(),
+ compilerCache = createCache(),
+ hasDuplicate = false,
+ sortOrder = function() { return 0; },
- // General-purpose constants
- strundefined = typeof undefined,
- MAX_NEGATIVE = 1 << 31,
+ // General-purpose constants
+ strundefined = typeof undefined,
+ MAX_NEGATIVE = 1 << 31,
- // Instance methods
- hasOwn = ({}).hasOwnProperty,
- arr = [],
- pop = arr.pop,
- push_native = arr.push,
- push = arr.push,
- slice = arr.slice,
- // Use a stripped-down indexOf if we can't use a native one
- indexOf = arr.indexOf || function( elem ) {
- var i = 0,
- len = this.length;
- for ( ; i < len; i++ ) {
- if ( this[i] === elem ) {
- return i;
- }
- }
- return -1;
- },
+ // Instance methods
+ hasOwn = ({}).hasOwnProperty,
+ arr = [],
+ pop = arr.pop,
+ push_native = arr.push,
+ push = arr.push,
+ slice = arr.slice,
+ // Use a stripped-down indexOf if we can't use a native one
+ indexOf = arr.indexOf || function( elem ) {
+ var i = 0,
+ len = this.length;
+ for ( ; i < len; i++ ) {
+ if ( this[i] === elem ) {
+ return i;
+ }
+ }
+ return -1;
+ },
- booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",
+ booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",
- // Regular expressions
+ // Regular expressions
- // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace
- whitespace = "[\\x20\\t\\r\\n\\f]",
- // http://www.w3.org/TR/css3-syntax/#characters
- characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",
+ // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace
+ whitespace = "[\\x20\\t\\r\\n\\f]",
+ // http://www.w3.org/TR/css3-syntax/#characters
+ characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",
- // Loosely modeled on CSS identifier characters
- // An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors
- // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
- identifier = characterEncoding.replace( "w", "w#" ),
+ // Loosely modeled on CSS identifier characters
+ // An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors
+ // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
+ identifier = characterEncoding.replace( "w", "w#" ),
- // Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors
- attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace +
- "*(?:([*^$|!~]?=)" + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]",
+ // Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors
+ attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace +
+ "*(?:([*^$|!~]?=)" + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]",
- // Prefer arguments quoted,
- // then not containing pseudos/brackets,
- // then attribute selectors/non-parenthetical expressions,
- // then anything else
- // These preferences are here to reduce the number of selectors
- // needing tokenize in the PSEUDO preFilter
- pseudos = ":(" + characterEncoding + ")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|" + attributes.replace( 3, 8 ) + ")*)|.*)\\)|)",
+ // Prefer arguments quoted,
+ // then not containing pseudos/brackets,
+ // then attribute selectors/non-parenthetical expressions,
+ // then anything else
+ // These preferences are here to reduce the number of selectors
+ // needing tokenize in the PSEUDO preFilter
+ pseudos = ":(" + characterEncoding + ")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|" + attributes.replace( 3, 8 ) + ")*)|.*)\\)|)",
- // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
- rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ),
+ // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
+ rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ),
- rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
- rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ),
+ rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
+ rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ),
- rsibling = new RegExp( whitespace + "*[+~]" ),
- rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*)" + whitespace + "*\\]", "g" ),
+ rsibling = new RegExp( whitespace + "*[+~]" ),
+ rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*)" + whitespace + "*\\]", "g" ),
- rpseudo = new RegExp( pseudos ),
- ridentifier = new RegExp( "^" + identifier + "$" ),
+ rpseudo = new RegExp( pseudos ),
+ ridentifier = new RegExp( "^" + identifier + "$" ),
- matchExpr = {
- "ID": new RegExp( "^#(" + characterEncoding + ")" ),
- "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ),
- "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ),
- "ATTR": new RegExp( "^" + attributes ),
- "PSEUDO": new RegExp( "^" + pseudos ),
- "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace +
- "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace +
- "*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
- "bool": new RegExp( "^(?:" + booleans + ")$", "i" ),
- // For use in libraries implementing .is()
- // We use this for POS matching in `select`
- "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" +
- whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" )
- },
+ matchExpr = {
+ "ID": new RegExp( "^#(" + characterEncoding + ")" ),
+ "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ),
+ "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ),
+ "ATTR": new RegExp( "^" + attributes ),
+ "PSEUDO": new RegExp( "^" + pseudos ),
+ "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace +
+ "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace +
+ "*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
+ "bool": new RegExp( "^(?:" + booleans + ")$", "i" ),
+ // For use in libraries implementing .is()
+ // We use this for POS matching in `select`
+ "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" +
+ whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" )
+ },
- rnative = /^[^{]+\{\s*\[native \w/,
+ rnative = /^[^{]+\{\s*\[native \w/,
- // Easily-parseable/retrievable ID or TAG or CLASS selectors
- rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,
+ // Easily-parseable/retrievable ID or TAG or CLASS selectors
+ rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,
- rinputs = /^(?:input|select|textarea|button)$/i,
- rheader = /^h\d$/i,
+ rinputs = /^(?:input|select|textarea|button)$/i,
+ rheader = /^h\d$/i,
- rescape = /'|\\/g,
+ rescape = /'|\\/g,
- // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters
- runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ),
- funescape = function( _, escaped, escapedWhitespace ) {
- var high = "0x" + escaped - 0x10000;
- // NaN means non-codepoint
- // Support: Firefox
- // Workaround erroneous numeric interpretation of +"0x"
- return high !== high || escapedWhitespace ?
- escaped :
- // BMP codepoint
- high < 0 ?
- String.fromCharCode( high + 0x10000 ) :
- // Supplemental Plane codepoint (surrogate pair)
- String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
- };
+ // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters
+ runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ),
+ funescape = function( _, escaped, escapedWhitespace ) {
+ var high = "0x" + escaped - 0x10000;
+ // NaN means non-codepoint
+ // Support: Firefox
+ // Workaround erroneous numeric interpretation of +"0x"
+ return high !== high || escapedWhitespace ?
+ escaped :
+ // BMP codepoint
+ high < 0 ?
+ String.fromCharCode( high + 0x10000 ) :
+ // Supplemental Plane codepoint (surrogate pair)
+ String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
+ };
// Optimize for push.apply( _, NodeList )
try {
- push.apply(
- (arr = slice.call( preferredDoc.childNodes )),
- preferredDoc.childNodes
- );
- // Support: Android<4.0
- // Detect silently failing push.apply
- arr[ preferredDoc.childNodes.length ].nodeType;
+ push.apply(
+ (arr = slice.call( preferredDoc.childNodes )),
+ preferredDoc.childNodes
+ );
+ // Support: Android<4.0
+ // Detect silently failing push.apply
+ arr[ preferredDoc.childNodes.length ].nodeType;
} catch ( e ) {
- push = { apply: arr.length ?
+ push = { apply: arr.length ?
- // Leverage slice if possible
- function( target, els ) {
- push_native.apply( target, slice.call(els) );
- } :
+ // Leverage slice if possible
+ function( target, els ) {
+ push_native.apply( target, slice.call(els) );
+ } :
- // Support: IE<9
- // Otherwise append directly
- function( target, els ) {
- var j = target.length,
- i = 0;
- // Can't trust NodeList.length
- while ( (target[j++] = els[i++]) ) {}
- target.length = j - 1;
- }
- };
+ // Support: IE<9
+ // Otherwise append directly
+ function( target, els ) {
+ var j = target.length,
+ i = 0;
+ // Can't trust NodeList.length
+ while ( (target[j++] = els[i++]) ) {}
+ target.length = j - 1;
+ }
+ };
}
function Sizzle( selector, context, results, seed ) {
- var match, elem, m, nodeType,
- // QSA vars
- i, groups, old, nid, newContext, newSelector;
+ var match, elem, m, nodeType,
+ // QSA vars
+ i, groups, old, nid, newContext, newSelector;
- if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) {
- setDocument( context );
- }
+ if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) {
+ setDocument( context );
+ }
- context = context || document;
- results = results || [];
+ context = context || document;
+ results = results || [];
- if ( !selector || typeof selector !== "string" ) {
- return results;
- }
+ if ( !selector || typeof selector !== "string" ) {
+ return results;
+ }
- if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) {
- return [];
- }
+ if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) {
+ return [];
+ }
- if ( documentIsHTML && !seed ) {
+ if ( documentIsHTML && !seed ) {
- // Shortcuts
- if ( (match = rquickExpr.exec( selector )) ) {
- // Speed-up: Sizzle("#ID")
- if ( (m = match[1]) ) {
- if ( nodeType === 9 ) {
- elem = context.getElementById( m );
- // Check parentNode to catch when Blackberry 4.6 returns
- // nodes that are no longer in the document #6963
- if ( elem && elem.parentNode ) {
- // Handle the case where IE, Opera, and Webkit return items
- // by name instead of ID
- if ( elem.id === m ) {
- results.push( elem );
- return results;
- }
- } else {
- return results;
- }
- } else {
- // Context is not a document
- if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) &&
- contains( context, elem ) && elem.id === m ) {
- results.push( elem );
- return results;
- }
- }
+ // Shortcuts
+ if ( (match = rquickExpr.exec( selector )) ) {
+ // Speed-up: Sizzle("#ID")
+ if ( (m = match[1]) ) {
+ if ( nodeType === 9 ) {
+ elem = context.getElementById( m );
+ // Check parentNode to catch when Blackberry 4.6 returns
+ // nodes that are no longer in the document #6963
+ if ( elem && elem.parentNode ) {
+ // Handle the case where IE, Opera, and Webkit return items
+ // by name instead of ID
+ if ( elem.id === m ) {
+ results.push( elem );
+ return results;
+ }
+ } else {
+ return results;
+ }
+ } else {
+ // Context is not a document
+ if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) &&
+ contains( context, elem ) && elem.id === m ) {
+ results.push( elem );
+ return results;
+ }
+ }
- // Speed-up: Sizzle("TAG")
- } else if ( match[2] ) {
- push.apply( results, context.getElementsByTagName( selector ) );
- return results;
+ // Speed-up: Sizzle("TAG")
+ } else if ( match[2] ) {
+ push.apply( results, context.getElementsByTagName( selector ) );
+ return results;
- // Speed-up: Sizzle(".CLASS")
- } else if ( (m = match[3]) && support.getElementsByClassName && context.getElementsByClassName ) {
- push.apply( results, context.getElementsByClassName( m ) );
- return results;
- }
- }
+ // Speed-up: Sizzle(".CLASS")
+ } else if ( (m = match[3]) && support.getElementsByClassName && context.getElementsByClassName ) {
+ push.apply( results, context.getElementsByClassName( m ) );
+ return results;
+ }
+ }
- // QSA path
- if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) {
- nid = old = expando;
- newContext = context;
- newSelector = nodeType === 9 && selector;
+ // QSA path
+ if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) {
+ nid = old = expando;
+ newContext = context;
+ newSelector = nodeType === 9 && selector;
- // qSA works strangely on Element-rooted queries
- // We can work around this by specifying an extra ID on the root
- // and working up from there (Thanks to Andrew Dupont for the technique)
- // IE 8 doesn't work on object elements
- if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
- groups = tokenize( selector );
+ // qSA works strangely on Element-rooted queries
+ // We can work around this by specifying an extra ID on the root
+ // and working up from there (Thanks to Andrew Dupont for the technique)
+ // IE 8 doesn't work on object elements
+ if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
+ groups = tokenize( selector );
- if ( (old = context.getAttribute("id")) ) {
- nid = old.replace( rescape, "\\$&" );
- } else {
- context.setAttribute( "id", nid );
- }
- nid = "[id='" + nid + "'] ";
+ if ( (old = context.getAttribute("id")) ) {
+ nid = old.replace( rescape, "\\$&" );
+ } else {
+ context.setAttribute( "id", nid );
+ }
+ nid = "[id='" + nid + "'] ";
- i = groups.length;
- while ( i-- ) {
- groups[i] = nid + toSelector( groups[i] );
- }
- newContext = rsibling.test( selector ) && context.parentNode || context;
- newSelector = groups.join(",");
- }
+ i = groups.length;
+ while ( i-- ) {
+ groups[i] = nid + toSelector( groups[i] );
+ }
+ newContext = rsibling.test( selector ) && context.parentNode || context;
+ newSelector = groups.join(",");
+ }
- if ( newSelector ) {
- try {
- push.apply( results,
- newContext.querySelectorAll( newSelector )
- );
- return results;
- } catch(qsaError) {
- } finally {
- if ( !old ) {
- context.removeAttribute("id");
- }
- }
- }
- }
- }
+ if ( newSelector ) {
+ try {
+ push.apply( results,
+ newContext.querySelectorAll( newSelector )
+ );
+ return results;
+ } catch(qsaError) {
+ } finally {
+ if ( !old ) {
+ context.removeAttribute("id");
+ }
+ }
+ }
+ }
+ }
- // All others
- return select( selector.replace( rtrim, "$1" ), context, results, seed );
+ // All others
+ return select( selector.replace( rtrim, "$1" ), context, results, seed );
}
/**
@@ -1153,27 +1153,27 @@ function Sizzle( selector, context, results, seed ) {
* @param {Function} fn The function to test for native support
*/
function isNative( fn ) {
- return rnative.test( fn + "" );
+ return rnative.test( fn + "" );
}
/**
* Create key-value caches of limited size
* @returns {Function(string, Object)} Returns the Object data after storing it on itself with
- * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)
- * deleting the oldest entry
+ * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)
+ * deleting the oldest entry
*/
function createCache() {
- var keys = [];
+ var keys = [];
- function cache( key, value ) {
- // Use (key + " ") to avoid collision with native prototype properties (see Issue #157)
- if ( keys.push( key += " " ) > Expr.cacheLength ) {
- // Only keep the most recent entries
- delete cache[ keys.shift() ];
- }
- return (cache[ key ] = value);
- }
- return cache;
+ function cache( key, value ) {
+ // Use (key + " ") to avoid collision with native prototype properties (see Issue #157)
+ if ( keys.push( key += " " ) > Expr.cacheLength ) {
+ // Only keep the most recent entries
+ delete cache[ keys.shift() ];
+ }
+ return (cache[ key ] = value);
+ }
+ return cache;
}
/**
@@ -1181,8 +1181,8 @@ function createCache() {
* @param {Function} fn The function to mark
*/
function markFunction( fn ) {
- fn[ expando ] = true;
- return fn;
+ fn[ expando ] = true;
+ return fn;
}
/**
@@ -1190,20 +1190,20 @@ function markFunction( fn ) {
* @param {Function} fn Passed the created div and expects a boolean result
*/
function assert( fn ) {
- var div = document.createElement("div");
+ var div = document.createElement("div");
- try {
- return !!fn( div );
- } catch (e) {
- return false;
- } finally {
- // Remove from its parent by default
- if ( div.parentNode ) {
- div.parentNode.removeChild( div );
- }
- // release memory in IE
- div = null;
- }
+ try {
+ return !!fn( div );
+ } catch (e) {
+ return false;
+ } finally {
+ // Remove from its parent by default
+ if ( div.parentNode ) {
+ div.parentNode.removeChild( div );
+ }
+ // release memory in IE
+ div = null;
+ }
}
/**
@@ -1213,17 +1213,17 @@ function assert( fn ) {
* @param {Boolean} test The result of a test. If true, null will be set as the handler in leiu of the specified handler
*/
function addHandle( attrs, handler, test ) {
- attrs = attrs.split("|");
- var current,
- i = attrs.length,
- setHandle = test ? null : handler;
+ attrs = attrs.split("|");
+ var current,
+ i = attrs.length,
+ setHandle = test ? null : handler;
- while ( i-- ) {
- // Don't override a user's handler
- if ( !(current = Expr.attrHandle[ attrs[i] ]) || current === handler ) {
- Expr.attrHandle[ attrs[i] ] = setHandle;
- }
- }
+ while ( i-- ) {
+ // Don't override a user's handler
+ if ( !(current = Expr.attrHandle[ attrs[i] ]) || current === handler ) {
+ Expr.attrHandle[ attrs[i] ] = setHandle;
+ }
+ }
}
/**
@@ -1232,11 +1232,11 @@ function addHandle( attrs, handler, test ) {
* @param {String} name
*/
function boolHandler( elem, name ) {
- // XML does not need to be checked as this will not be assigned for XML documents
- var val = elem.getAttributeNode( name );
- return val && val.specified ?
- val.value :
- elem[ name ] === true ? name.toLowerCase() : null;
+ // XML does not need to be checked as this will not be assigned for XML documents
+ var val = elem.getAttributeNode( name );
+ return val && val.specified ?
+ val.value :
+ elem[ name ] === true ? name.toLowerCase() : null;
}
/**
@@ -1246,8 +1246,8 @@ function boolHandler( elem, name ) {
* @param {String} name
*/
function interpolationHandler( elem, name ) {
- // XML does not need to be checked as this will not be assigned for XML documents
- return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 );
+ // XML does not need to be checked as this will not be assigned for XML documents
+ return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 );
}
/**
@@ -1256,12 +1256,12 @@ function interpolationHandler( elem, name ) {
* @param {String} name
*/
function valueHandler( elem ) {
- // Ignore the value *property* on inputs by using defaultValue
- // Fallback to Sizzle.attr by returning undefined where appropriate
- // XML does not need to be checked as this will not be assigned for XML documents
- if ( elem.nodeName.toLowerCase() === "input" ) {
- return elem.defaultValue;
- }
+ // Ignore the value *property* on inputs by using defaultValue
+ // Fallback to Sizzle.attr by returning undefined where appropriate
+ // XML does not need to be checked as this will not be assigned for XML documents
+ if ( elem.nodeName.toLowerCase() === "input" ) {
+ return elem.defaultValue;
+ }
}
/**
@@ -1271,26 +1271,26 @@ function valueHandler( elem ) {
* @returns Returns -1 if a precedes b, 1 if a follows b
*/
function siblingCheck( a, b ) {
- var cur = b && a,
- diff = cur && a.nodeType === 1 && b.nodeType === 1 &&
- ( ~b.sourceIndex || MAX_NEGATIVE ) -
- ( ~a.sourceIndex || MAX_NEGATIVE );
+ var cur = b && a,
+ diff = cur && a.nodeType === 1 && b.nodeType === 1 &&
+ ( ~b.sourceIndex || MAX_NEGATIVE ) -
+ ( ~a.sourceIndex || MAX_NEGATIVE );
- // Use IE sourceIndex if available on both nodes
- if ( diff ) {
- return diff;
- }
+ // Use IE sourceIndex if available on both nodes
+ if ( diff ) {
+ return diff;
+ }
- // Check if b follows a
- if ( cur ) {
- while ( (cur = cur.nextSibling) ) {
- if ( cur === b ) {
- return -1;
- }
- }
- }
+ // Check if b follows a
+ if ( cur ) {
+ while ( (cur = cur.nextSibling) ) {
+ if ( cur === b ) {
+ return -1;
+ }
+ }
+ }
- return a ? 1 : -1;
+ return a ? 1 : -1;
}
/**
@@ -1298,10 +1298,10 @@ function siblingCheck( a, b ) {
* @param {String} type
*/
function createInputPseudo( type ) {
- return function( elem ) {
- var name = elem.nodeName.toLowerCase();
- return name === "input" && elem.type === type;
- };
+ return function( elem ) {
+ var name = elem.nodeName.toLowerCase();
+ return name === "input" && elem.type === type;
+ };
}
/**
@@ -1309,10 +1309,10 @@ function createInputPseudo( type ) {
* @param {String} type
*/
function createButtonPseudo( type ) {
- return function( elem ) {
- var name = elem.nodeName.toLowerCase();
- return (name === "input" || name === "button") && elem.type === type;
- };
+ return function( elem ) {
+ var name = elem.nodeName.toLowerCase();
+ return (name === "input" || name === "button") && elem.type === type;
+ };
}
/**
@@ -1320,21 +1320,21 @@ function createButtonPseudo( type ) {
* @param {Function} fn
*/
function createPositionalPseudo( fn ) {
- return markFunction(function( argument ) {
- argument = +argument;
- return markFunction(function( seed, matches ) {
- var j,
- matchIndexes = fn( [], seed.length, argument ),
- i = matchIndexes.length;
+ return markFunction(function( argument ) {
+ argument = +argument;
+ return markFunction(function( seed, matches ) {
+ var j,
+ matchIndexes = fn( [], seed.length, argument ),
+ i = matchIndexes.length;
- // Match elements found at the specified indexes
- while ( i-- ) {
- if ( seed[ (j = matchIndexes[i]) ] ) {
- seed[j] = !(matches[j] = seed[j]);
- }
- }
- });
- });
+ // Match elements found at the specified indexes
+ while ( i-- ) {
+ if ( seed[ (j = matchIndexes[i]) ] ) {
+ seed[j] = !(matches[j] = seed[j]);
+ }
+ }
+ });
+ });
}
/**
@@ -1342,10 +1342,10 @@ function createPositionalPseudo( fn ) {
* @param {Element|Object} elem An element or a document
*/
isXML = Sizzle.isXML = function( elem ) {
- // documentElement is verified for cases where it doesn't yet exist
- // (such as loading iframes in IE - #4833)
- var documentElement = elem && (elem.ownerDocument || elem).documentElement;
- return documentElement ? documentElement.nodeName !== "HTML" : false;
+ // documentElement is verified for cases where it doesn't yet exist
+ // (such as loading iframes in IE - #4833)
+ var documentElement = elem && (elem.ownerDocument || elem).documentElement;
+ return documentElement ? documentElement.nodeName !== "HTML" : false;
};
// Expose support vars for convenience
@@ -1357,436 +1357,436 @@ support = Sizzle.support = {};
* @returns {Object} Returns the current document
*/
setDocument = Sizzle.setDocument = function( node ) {
- var doc = node ? node.ownerDocument || node : preferredDoc,
- parent = doc.parentWindow;
+ var doc = node ? node.ownerDocument || node : preferredDoc,
+ parent = doc.parentWindow;
- // If no document and documentElement is available, return
- if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) {
- return document;
- }
+ // If no document and documentElement is available, return
+ if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) {
+ return document;
+ }
- // Set our document
- document = doc;
- docElem = doc.documentElement;
+ // Set our document
+ document = doc;
+ docElem = doc.documentElement;
- // Support tests
- documentIsHTML = !isXML( doc );
+ // Support tests
+ documentIsHTML = !isXML( doc );
- // Support: IE>8
- // If iframe document is assigned to "document" variable and if iframe has been reloaded,
- // IE will throw "permission denied" error when accessing "document" variable, see jQuery #13936
- if ( parent && parent.frameElement ) {
- parent.attachEvent( "onbeforeunload", function() {
- setDocument();
- });
- }
+ // Support: IE>8
+ // If iframe document is assigned to "document" variable and if iframe has been reloaded,
+ // IE will throw "permission denied" error when accessing "document" variable, see jQuery #13936
+ if ( parent && parent.frameElement ) {
+ parent.attachEvent( "onbeforeunload", function() {
+ setDocument();
+ });
+ }
- /* Attributes
- ---------------------------------------------------------------------- */
+ /* Attributes
+ ---------------------------------------------------------------------- */
- // Support: IE<8
- // Verify that getAttribute really returns attributes and not properties (excepting IE8 booleans)
- support.attributes = assert(function( div ) {
+ // Support: IE<8
+ // Verify that getAttribute really returns attributes and not properties (excepting IE8 booleans)
+ support.attributes = assert(function( div ) {
- // Support: IE<8
- // Prevent attribute/property "interpolation"
- div.innerHTML = "";
- addHandle( "type|href|height|width", interpolationHandler, div.firstChild.getAttribute("href") === "#" );
+ // Support: IE<8
+ // Prevent attribute/property "interpolation"
+ div.innerHTML = "";
+ addHandle( "type|href|height|width", interpolationHandler, div.firstChild.getAttribute("href") === "#" );
- // Support: IE<9
- // Use getAttributeNode to fetch booleans when getAttribute lies
- addHandle( booleans, boolHandler, div.getAttribute("disabled") == null );
+ // Support: IE<9
+ // Use getAttributeNode to fetch booleans when getAttribute lies
+ addHandle( booleans, boolHandler, div.getAttribute("disabled") == null );
- div.className = "i";
- return !div.getAttribute("className");
- });
+ div.className = "i";
+ return !div.getAttribute("className");
+ });
- // Support: IE<9
- // Retrieving value should defer to defaultValue
- support.input = assert(function( div ) {
- div.innerHTML = "";
- div.firstChild.setAttribute( "value", "" );
- return div.firstChild.getAttribute( "value" ) === "";
- });
+ // Support: IE<9
+ // Retrieving value should defer to defaultValue
+ support.input = assert(function( div ) {
+ div.innerHTML = "";
+ div.firstChild.setAttribute( "value", "" );
+ return div.firstChild.getAttribute( "value" ) === "";
+ });
- // IE6/7 still return empty string for value,
- // but are actually retrieving the property
- addHandle( "value", valueHandler, support.attributes && support.input );
+ // IE6/7 still return empty string for value,
+ // but are actually retrieving the property
+ addHandle( "value", valueHandler, support.attributes && support.input );
- /* getElement(s)By*
- ---------------------------------------------------------------------- */
+ /* getElement(s)By*
+ ---------------------------------------------------------------------- */
- // Check if getElementsByTagName("*") returns only elements
- support.getElementsByTagName = assert(function( div ) {
- div.appendChild( doc.createComment("") );
- return !div.getElementsByTagName("*").length;
- });
+ // Check if getElementsByTagName("*") returns only elements
+ support.getElementsByTagName = assert(function( div ) {
+ div.appendChild( doc.createComment("") );
+ return !div.getElementsByTagName("*").length;
+ });
- // Check if getElementsByClassName can be trusted
- support.getElementsByClassName = assert(function( div ) {
- div.innerHTML = "";
+ // Check if getElementsByClassName can be trusted
+ support.getElementsByClassName = assert(function( div ) {
+ div.innerHTML = "";
- // Support: Safari<4
- // Catch class over-caching
- div.firstChild.className = "i";
- // Support: Opera<10
- // Catch gEBCN failure to find non-leading classes
- return div.getElementsByClassName("i").length === 2;
- });
+ // Support: Safari<4
+ // Catch class over-caching
+ div.firstChild.className = "i";
+ // Support: Opera<10
+ // Catch gEBCN failure to find non-leading classes
+ return div.getElementsByClassName("i").length === 2;
+ });
- // Support: IE<10
- // Check if getElementById returns elements by name
- // The broken getElementById methods don't pick up programatically-set names,
- // so use a roundabout getElementsByName test
- support.getById = assert(function( div ) {
- docElem.appendChild( div ).id = expando;
- return !doc.getElementsByName || !doc.getElementsByName( expando ).length;
- });
+ // Support: IE<10
+ // Check if getElementById returns elements by name
+ // The broken getElementById methods don't pick up programatically-set names,
+ // so use a roundabout getElementsByName test
+ support.getById = assert(function( div ) {
+ docElem.appendChild( div ).id = expando;
+ return !doc.getElementsByName || !doc.getElementsByName( expando ).length;
+ });
- // ID find and filter
- if ( support.getById ) {
- Expr.find["ID"] = function( id, context ) {
- if ( typeof context.getElementById !== strundefined && documentIsHTML ) {
- var m = context.getElementById( id );
- // Check parentNode to catch when Blackberry 4.6 returns
- // nodes that are no longer in the document #6963
- return m && m.parentNode ? [m] : [];
- }
- };
- Expr.filter["ID"] = function( id ) {
- var attrId = id.replace( runescape, funescape );
- return function( elem ) {
- return elem.getAttribute("id") === attrId;
- };
- };
- } else {
- // Support: IE6/7
- // getElementById is not reliable as a find shortcut
- delete Expr.find["ID"];
+ // ID find and filter
+ if ( support.getById ) {
+ Expr.find["ID"] = function( id, context ) {
+ if ( typeof context.getElementById !== strundefined && documentIsHTML ) {
+ var m = context.getElementById( id );
+ // Check parentNode to catch when Blackberry 4.6 returns
+ // nodes that are no longer in the document #6963
+ return m && m.parentNode ? [m] : [];
+ }
+ };
+ Expr.filter["ID"] = function( id ) {
+ var attrId = id.replace( runescape, funescape );
+ return function( elem ) {
+ return elem.getAttribute("id") === attrId;
+ };
+ };
+ } else {
+ // Support: IE6/7
+ // getElementById is not reliable as a find shortcut
+ delete Expr.find["ID"];
- Expr.filter["ID"] = function( id ) {
- var attrId = id.replace( runescape, funescape );
- return function( elem ) {
- var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id");
- return node && node.value === attrId;
- };
- };
- }
+ Expr.filter["ID"] = function( id ) {
+ var attrId = id.replace( runescape, funescape );
+ return function( elem ) {
+ var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id");
+ return node && node.value === attrId;
+ };
+ };
+ }
- // Tag
- Expr.find["TAG"] = support.getElementsByTagName ?
- function( tag, context ) {
- if ( typeof context.getElementsByTagName !== strundefined ) {
- return context.getElementsByTagName( tag );
- }
- } :
- function( tag, context ) {
- var elem,
- tmp = [],
- i = 0,
- results = context.getElementsByTagName( tag );
+ // Tag
+ Expr.find["TAG"] = support.getElementsByTagName ?
+ function( tag, context ) {
+ if ( typeof context.getElementsByTagName !== strundefined ) {
+ return context.getElementsByTagName( tag );
+ }
+ } :
+ function( tag, context ) {
+ var elem,
+ tmp = [],
+ i = 0,
+ results = context.getElementsByTagName( tag );
- // Filter out possible comments
- if ( tag === "*" ) {
- while ( (elem = results[i++]) ) {
- if ( elem.nodeType === 1 ) {
- tmp.push( elem );
- }
- }
+ // Filter out possible comments
+ if ( tag === "*" ) {
+ while ( (elem = results[i++]) ) {
+ if ( elem.nodeType === 1 ) {
+ tmp.push( elem );
+ }
+ }
- return tmp;
- }
- return results;
- };
+ return tmp;
+ }
+ return results;
+ };
- // Class
- Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) {
- if ( typeof context.getElementsByClassName !== strundefined && documentIsHTML ) {
- return context.getElementsByClassName( className );
- }
- };
+ // Class
+ Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) {
+ if ( typeof context.getElementsByClassName !== strundefined && documentIsHTML ) {
+ return context.getElementsByClassName( className );
+ }
+ };
- /* QSA/matchesSelector
- ---------------------------------------------------------------------- */
+ /* QSA/matchesSelector
+ ---------------------------------------------------------------------- */
- // QSA and matchesSelector support
+ // QSA and matchesSelector support
- // matchesSelector(:active) reports false when true (IE9/Opera 11.5)
- rbuggyMatches = [];
+ // matchesSelector(:active) reports false when true (IE9/Opera 11.5)
+ rbuggyMatches = [];
- // qSa(:focus) reports false when true (Chrome 21)
- // We allow this because of a bug in IE8/9 that throws an error
- // whenever `document.activeElement` is accessed on an iframe
- // So, we allow :focus to pass through QSA all the time to avoid the IE error
- // See http://bugs.jquery.com/ticket/13378
- rbuggyQSA = [];
+ // qSa(:focus) reports false when true (Chrome 21)
+ // We allow this because of a bug in IE8/9 that throws an error
+ // whenever `document.activeElement` is accessed on an iframe
+ // So, we allow :focus to pass through QSA all the time to avoid the IE error
+ // See http://bugs.jquery.com/ticket/13378
+ rbuggyQSA = [];
- if ( (support.qsa = isNative(doc.querySelectorAll)) ) {
- // Build QSA regex
- // Regex strategy adopted from Diego Perini
- assert(function( div ) {
- // Select is set to empty string on purpose
- // This is to test IE's treatment of not explicitly
- // setting a boolean content attribute,
- // since its presence should be enough
- // http://bugs.jquery.com/ticket/12359
- div.innerHTML = "";
+ if ( (support.qsa = isNative(doc.querySelectorAll)) ) {
+ // Build QSA regex
+ // Regex strategy adopted from Diego Perini
+ assert(function( div ) {
+ // Select is set to empty string on purpose
+ // This is to test IE's treatment of not explicitly
+ // setting a boolean content attribute,
+ // since its presence should be enough
+ // http://bugs.jquery.com/ticket/12359
+ div.innerHTML = "";
- // Support: IE8
- // Boolean attributes and "value" are not treated correctly
- if ( !div.querySelectorAll("[selected]").length ) {
- rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" );
- }
+ // Support: IE8
+ // Boolean attributes and "value" are not treated correctly
+ if ( !div.querySelectorAll("[selected]").length ) {
+ rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" );
+ }
- // Webkit/Opera - :checked should return selected option elements
- // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
- // IE8 throws error here and will not see later tests
- if ( !div.querySelectorAll(":checked").length ) {
- rbuggyQSA.push(":checked");
- }
- });
+ // Webkit/Opera - :checked should return selected option elements
+ // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
+ // IE8 throws error here and will not see later tests
+ if ( !div.querySelectorAll(":checked").length ) {
+ rbuggyQSA.push(":checked");
+ }
+ });
- assert(function( div ) {
+ assert(function( div ) {
- // Support: Opera 10-12/IE8
- // ^= $= *= and empty values
- // Should not select anything
- // Support: Windows 8 Native Apps
- // The type attribute is restricted during .innerHTML assignment
- var input = doc.createElement("input");
- input.setAttribute( "type", "hidden" );
- div.appendChild( input ).setAttribute( "t", "" );
+ // Support: Opera 10-12/IE8
+ // ^= $= *= and empty values
+ // Should not select anything
+ // Support: Windows 8 Native Apps
+ // The type attribute is restricted during .innerHTML assignment
+ var input = doc.createElement("input");
+ input.setAttribute( "type", "hidden" );
+ div.appendChild( input ).setAttribute( "t", "" );
- if ( div.querySelectorAll("[t^='']").length ) {
- rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" );
- }
+ if ( div.querySelectorAll("[t^='']").length ) {
+ rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" );
+ }
- // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)
- // IE8 throws error here and will not see later tests
- if ( !div.querySelectorAll(":enabled").length ) {
- rbuggyQSA.push( ":enabled", ":disabled" );
- }
+ // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)
+ // IE8 throws error here and will not see later tests
+ if ( !div.querySelectorAll(":enabled").length ) {
+ rbuggyQSA.push( ":enabled", ":disabled" );
+ }
- // Opera 10-11 does not throw on post-comma invalid pseudos
- div.querySelectorAll("*,:x");
- rbuggyQSA.push(",.*:");
- });
- }
+ // Opera 10-11 does not throw on post-comma invalid pseudos
+ div.querySelectorAll("*,:x");
+ rbuggyQSA.push(",.*:");
+ });
+ }
- if ( (support.matchesSelector = isNative( (matches = docElem.webkitMatchesSelector ||
- docElem.mozMatchesSelector ||
- docElem.oMatchesSelector ||
- docElem.msMatchesSelector) )) ) {
+ if ( (support.matchesSelector = isNative( (matches = docElem.webkitMatchesSelector ||
+ docElem.mozMatchesSelector ||
+ docElem.oMatchesSelector ||
+ docElem.msMatchesSelector) )) ) {
- assert(function( div ) {
- // Check to see if it's possible to do matchesSelector
- // on a disconnected node (IE 9)
- support.disconnectedMatch = matches.call( div, "div" );
+ assert(function( div ) {
+ // Check to see if it's possible to do matchesSelector
+ // on a disconnected node (IE 9)
+ support.disconnectedMatch = matches.call( div, "div" );
- // This should fail with an exception
- // Gecko does not error, returns false instead
- matches.call( div, "[s!='']:x" );
- rbuggyMatches.push( "!=", pseudos );
- });
- }
+ // This should fail with an exception
+ // Gecko does not error, returns false instead
+ matches.call( div, "[s!='']:x" );
+ rbuggyMatches.push( "!=", pseudos );
+ });
+ }
- rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") );
- rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") );
+ rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") );
+ rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") );
- /* Contains
- ---------------------------------------------------------------------- */
+ /* Contains
+ ---------------------------------------------------------------------- */
- // Element contains another
- // Purposefully does not implement inclusive descendent
- // As in, an element does not contain itself
- contains = isNative(docElem.contains) || docElem.compareDocumentPosition ?
- function( a, b ) {
- var adown = a.nodeType === 9 ? a.documentElement : a,
- bup = b && b.parentNode;
- return a === bup || !!( bup && bup.nodeType === 1 && (
- adown.contains ?
- adown.contains( bup ) :
- a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
- ));
- } :
- function( a, b ) {
- if ( b ) {
- while ( (b = b.parentNode) ) {
- if ( b === a ) {
- return true;
- }
- }
- }
- return false;
- };
+ // Element contains another
+ // Purposefully does not implement inclusive descendent
+ // As in, an element does not contain itself
+ contains = isNative(docElem.contains) || docElem.compareDocumentPosition ?
+ function( a, b ) {
+ var adown = a.nodeType === 9 ? a.documentElement : a,
+ bup = b && b.parentNode;
+ return a === bup || !!( bup && bup.nodeType === 1 && (
+ adown.contains ?
+ adown.contains( bup ) :
+ a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
+ ));
+ } :
+ function( a, b ) {
+ if ( b ) {
+ while ( (b = b.parentNode) ) {
+ if ( b === a ) {
+ return true;
+ }
+ }
+ }
+ return false;
+ };
- /* Sorting
- ---------------------------------------------------------------------- */
+ /* Sorting
+ ---------------------------------------------------------------------- */
- // Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27)
- // Detached nodes confoundingly follow *each other*
- support.sortDetached = assert(function( div1 ) {
- // Should return 1, but returns 4 (following)
- return div1.compareDocumentPosition( doc.createElement("div") ) & 1;
- });
+ // Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27)
+ // Detached nodes confoundingly follow *each other*
+ support.sortDetached = assert(function( div1 ) {
+ // Should return 1, but returns 4 (following)
+ return div1.compareDocumentPosition( doc.createElement("div") ) & 1;
+ });
- // Document order sorting
- sortOrder = docElem.compareDocumentPosition ?
- function( a, b ) {
+ // Document order sorting
+ sortOrder = docElem.compareDocumentPosition ?
+ function( a, b ) {
- // Flag for duplicate removal
- if ( a === b ) {
- hasDuplicate = true;
- return 0;
- }
+ // Flag for duplicate removal
+ if ( a === b ) {
+ hasDuplicate = true;
+ return 0;
+ }
- var compare = b.compareDocumentPosition && a.compareDocumentPosition && a.compareDocumentPosition( b );
+ var compare = b.compareDocumentPosition && a.compareDocumentPosition && a.compareDocumentPosition( b );
- if ( compare ) {
- // Disconnected nodes
- if ( compare & 1 ||
- (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) {
+ if ( compare ) {
+ // Disconnected nodes
+ if ( compare & 1 ||
+ (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) {
- // Choose the first element that is related to our preferred document
- if ( a === doc || contains(preferredDoc, a) ) {
- return -1;
- }
- if ( b === doc || contains(preferredDoc, b) ) {
- return 1;
- }
+ // Choose the first element that is related to our preferred document
+ if ( a === doc || contains(preferredDoc, a) ) {
+ return -1;
+ }
+ if ( b === doc || contains(preferredDoc, b) ) {
+ return 1;
+ }
- // Maintain original order
- return sortInput ?
- ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) :
- 0;
- }
+ // Maintain original order
+ return sortInput ?
+ ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) :
+ 0;
+ }
- return compare & 4 ? -1 : 1;
- }
+ return compare & 4 ? -1 : 1;
+ }
- // Not directly comparable, sort on existence of method
- return a.compareDocumentPosition ? -1 : 1;
- } :
- function( a, b ) {
- var cur,
- i = 0,
- aup = a.parentNode,
- bup = b.parentNode,
- ap = [ a ],
- bp = [ b ];
+ // Not directly comparable, sort on existence of method
+ return a.compareDocumentPosition ? -1 : 1;
+ } :
+ function( a, b ) {
+ var cur,
+ i = 0,
+ aup = a.parentNode,
+ bup = b.parentNode,
+ ap = [ a ],
+ bp = [ b ];
- // Exit early if the nodes are identical
- if ( a === b ) {
- hasDuplicate = true;
- return 0;
+ // Exit early if the nodes are identical
+ if ( a === b ) {
+ hasDuplicate = true;
+ return 0;
- // Parentless nodes are either documents or disconnected
- } else if ( !aup || !bup ) {
- return a === doc ? -1 :
- b === doc ? 1 :
- aup ? -1 :
- bup ? 1 :
- sortInput ?
- ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) :
- 0;
+ // Parentless nodes are either documents or disconnected
+ } else if ( !aup || !bup ) {
+ return a === doc ? -1 :
+ b === doc ? 1 :
+ aup ? -1 :
+ bup ? 1 :
+ sortInput ?
+ ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) :
+ 0;
- // If the nodes are siblings, we can do a quick check
- } else if ( aup === bup ) {
- return siblingCheck( a, b );
- }
+ // If the nodes are siblings, we can do a quick check
+ } else if ( aup === bup ) {
+ return siblingCheck( a, b );
+ }
- // Otherwise we need full lists of their ancestors for comparison
- cur = a;
- while ( (cur = cur.parentNode) ) {
- ap.unshift( cur );
- }
- cur = b;
- while ( (cur = cur.parentNode) ) {
- bp.unshift( cur );
- }
+ // Otherwise we need full lists of their ancestors for comparison
+ cur = a;
+ while ( (cur = cur.parentNode) ) {
+ ap.unshift( cur );
+ }
+ cur = b;
+ while ( (cur = cur.parentNode) ) {
+ bp.unshift( cur );
+ }
- // Walk down the tree looking for a discrepancy
- while ( ap[i] === bp[i] ) {
- i++;
- }
+ // Walk down the tree looking for a discrepancy
+ while ( ap[i] === bp[i] ) {
+ i++;
+ }
- return i ?
- // Do a sibling check if the nodes have a common ancestor
- siblingCheck( ap[i], bp[i] ) :
+ return i ?
+ // Do a sibling check if the nodes have a common ancestor
+ siblingCheck( ap[i], bp[i] ) :
- // Otherwise nodes in our document sort first
- ap[i] === preferredDoc ? -1 :
- bp[i] === preferredDoc ? 1 :
- 0;
- };
+ // Otherwise nodes in our document sort first
+ ap[i] === preferredDoc ? -1 :
+ bp[i] === preferredDoc ? 1 :
+ 0;
+ };
- return doc;
+ return doc;
};
Sizzle.matches = function( expr, elements ) {
- return Sizzle( expr, null, null, elements );
+ return Sizzle( expr, null, null, elements );
};
Sizzle.matchesSelector = function( elem, expr ) {
- // Set document vars if needed
- if ( ( elem.ownerDocument || elem ) !== document ) {
- setDocument( elem );
- }
+ // Set document vars if needed
+ if ( ( elem.ownerDocument || elem ) !== document ) {
+ setDocument( elem );
+ }
- // Make sure that attribute selectors are quoted
- expr = expr.replace( rattributeQuotes, "='$1']" );
+ // Make sure that attribute selectors are quoted
+ expr = expr.replace( rattributeQuotes, "='$1']" );
- if ( support.matchesSelector && documentIsHTML &&
- ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) &&
- ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) {
+ if ( support.matchesSelector && documentIsHTML &&
+ ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) &&
+ ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) {
- try {
- var ret = matches.call( elem, expr );
+ try {
+ var ret = matches.call( elem, expr );
- // IE 9's matchesSelector returns false on disconnected nodes
- if ( ret || support.disconnectedMatch ||
- // As well, disconnected nodes are said to be in a document
- // fragment in IE 9
- elem.document && elem.document.nodeType !== 11 ) {
- return ret;
- }
- } catch(e) {}
- }
+ // IE 9's matchesSelector returns false on disconnected nodes
+ if ( ret || support.disconnectedMatch ||
+ // As well, disconnected nodes are said to be in a document
+ // fragment in IE 9
+ elem.document && elem.document.nodeType !== 11 ) {
+ return ret;
+ }
+ } catch(e) {}
+ }
- return Sizzle( expr, document, null, [elem] ).length > 0;
+ return Sizzle( expr, document, null, [elem] ).length > 0;
};
Sizzle.contains = function( context, elem ) {
- // Set document vars if needed
- if ( ( context.ownerDocument || context ) !== document ) {
- setDocument( context );
- }
- return contains( context, elem );
+ // Set document vars if needed
+ if ( ( context.ownerDocument || context ) !== document ) {
+ setDocument( context );
+ }
+ return contains( context, elem );
};
Sizzle.attr = function( elem, name ) {
- // Set document vars if needed
- if ( ( elem.ownerDocument || elem ) !== document ) {
- setDocument( elem );
- }
+ // Set document vars if needed
+ if ( ( elem.ownerDocument || elem ) !== document ) {
+ setDocument( elem );
+ }
- var fn = Expr.attrHandle[ name.toLowerCase() ],
- // Don't get fooled by Object.prototype properties (jQuery #13807)
- val = ( fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ?
- fn( elem, name, !documentIsHTML ) :
- undefined );
+ var fn = Expr.attrHandle[ name.toLowerCase() ],
+ // Don't get fooled by Object.prototype properties (jQuery #13807)
+ val = ( fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ?
+ fn( elem, name, !documentIsHTML ) :
+ undefined );
- return val === undefined ?
- support.attributes || !documentIsHTML ?
- elem.getAttribute( name ) :
- (val = elem.getAttributeNode(name)) && val.specified ?
- val.value :
- null :
- val;
+ return val === undefined ?
+ support.attributes || !documentIsHTML ?
+ elem.getAttribute( name ) :
+ (val = elem.getAttributeNode(name)) && val.specified ?
+ val.value :
+ null :
+ val;
};
Sizzle.error = function( msg ) {
- throw new Error( "Syntax error, unrecognized expression: " + msg );
+ throw new Error( "Syntax error, unrecognized expression: " + msg );
};
/**
@@ -1794,28 +1794,28 @@ Sizzle.error = function( msg ) {
* @param {ArrayLike} results
*/
Sizzle.uniqueSort = function( results ) {
- var elem,
- duplicates = [],
- j = 0,
- i = 0;
+ var elem,
+ duplicates = [],
+ j = 0,
+ i = 0;
- // Unless we *know* we can detect duplicates, assume their presence
- hasDuplicate = !support.detectDuplicates;
- sortInput = !support.sortStable && results.slice( 0 );
- results.sort( sortOrder );
+ // Unless we *know* we can detect duplicates, assume their presence
+ hasDuplicate = !support.detectDuplicates;
+ sortInput = !support.sortStable && results.slice( 0 );
+ results.sort( sortOrder );
- if ( hasDuplicate ) {
- while ( (elem = results[i++]) ) {
- if ( elem === results[ i ] ) {
- j = duplicates.push( i );
- }
- }
- while ( j-- ) {
- results.splice( duplicates[ j ], 1 );
- }
- }
+ if ( hasDuplicate ) {
+ while ( (elem = results[i++]) ) {
+ if ( elem === results[ i ] ) {
+ j = duplicates.push( i );
+ }
+ }
+ while ( j-- ) {
+ results.splice( duplicates[ j ], 1 );
+ }
+ }
- return results;
+ return results;
};
/**
@@ -1823,1019 +1823,1019 @@ Sizzle.uniqueSort = function( results ) {
* @param {Array|Element} elem
*/
getText = Sizzle.getText = function( elem ) {
- var node,
- ret = "",
- i = 0,
- nodeType = elem.nodeType;
+ var node,
+ ret = "",
+ i = 0,
+ nodeType = elem.nodeType;
- if ( !nodeType ) {
- // If no nodeType, this is expected to be an array
- for ( ; (node = elem[i]); i++ ) {
- // Do not traverse comment nodes
- ret += getText( node );
- }
- } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
- // Use textContent for elements
- // innerText usage removed for consistency of new lines (see #11153)
- if ( typeof elem.textContent === "string" ) {
- return elem.textContent;
- } else {
- // Traverse its children
- for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
- ret += getText( elem );
- }
- }
- } else if ( nodeType === 3 || nodeType === 4 ) {
- return elem.nodeValue;
- }
- // Do not include comment or processing instruction nodes
+ if ( !nodeType ) {
+ // If no nodeType, this is expected to be an array
+ for ( ; (node = elem[i]); i++ ) {
+ // Do not traverse comment nodes
+ ret += getText( node );
+ }
+ } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
+ // Use textContent for elements
+ // innerText usage removed for consistency of new lines (see #11153)
+ if ( typeof elem.textContent === "string" ) {
+ return elem.textContent;
+ } else {
+ // Traverse its children
+ for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
+ ret += getText( elem );
+ }
+ }
+ } else if ( nodeType === 3 || nodeType === 4 ) {
+ return elem.nodeValue;
+ }
+ // Do not include comment or processing instruction nodes
- return ret;
+ return ret;
};
Expr = Sizzle.selectors = {
- // Can be adjusted by the user
- cacheLength: 50,
-
- createPseudo: markFunction,
-
- match: matchExpr,
-
- attrHandle: {},
-
- find: {},
-
- relative: {
- ">": { dir: "parentNode", first: true },
- " ": { dir: "parentNode" },
- "+": { dir: "previousSibling", first: true },
- "~": { dir: "previousSibling" }
- },
-
- preFilter: {
- "ATTR": function( match ) {
- match[1] = match[1].replace( runescape, funescape );
-
- // Move the given value to match[3] whether quoted or unquoted
- match[3] = ( match[4] || match[5] || "" ).replace( runescape, funescape );
-
- if ( match[2] === "~=" ) {
- match[3] = " " + match[3] + " ";
- }
-
- return match.slice( 0, 4 );
- },
-
- "CHILD": function( match ) {
- /* matches from matchExpr["CHILD"]
- 1 type (only|nth|...)
- 2 what (child|of-type)
- 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
- 4 xn-component of xn+y argument ([+-]?\d*n|)
- 5 sign of xn-component
- 6 x of xn-component
- 7 sign of y-component
- 8 y of y-component
- */
- match[1] = match[1].toLowerCase();
-
- if ( match[1].slice( 0, 3 ) === "nth" ) {
- // nth-* requires argument
- if ( !match[3] ) {
- Sizzle.error( match[0] );
- }
-
- // numeric x and y parameters for Expr.filter.CHILD
- // remember that false/true cast respectively to 0/1
- match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) );
- match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" );
-
- // other types prohibit arguments
- } else if ( match[3] ) {
- Sizzle.error( match[0] );
- }
-
- return match;
- },
-
- "PSEUDO": function( match ) {
- var excess,
- unquoted = !match[5] && match[2];
-
- if ( matchExpr["CHILD"].test( match[0] ) ) {
- return null;
- }
-
- // Accept quoted arguments as-is
- if ( match[3] && match[4] !== undefined ) {
- match[2] = match[4];
-
- // Strip excess characters from unquoted arguments
- } else if ( unquoted && rpseudo.test( unquoted ) &&
- // Get excess from tokenize (recursively)
- (excess = tokenize( unquoted, true )) &&
- // advance to the next closing parenthesis
- (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) {
-
- // excess is a negative index
- match[0] = match[0].slice( 0, excess );
- match[2] = unquoted.slice( 0, excess );
- }
-
- // Return only captures needed by the pseudo filter method (type and argument)
- return match.slice( 0, 3 );
- }
- },
-
- filter: {
-
- "TAG": function( nodeNameSelector ) {
- var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase();
- return nodeNameSelector === "*" ?
- function() { return true; } :
- function( elem ) {
- return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
- };
- },
-
- "CLASS": function( className ) {
- var pattern = classCache[ className + " " ];
-
- return pattern ||
- (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) &&
- classCache( className, function( elem ) {
- return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== strundefined && elem.getAttribute("class") || "" );
- });
- },
-
- "ATTR": function( name, operator, check ) {
- return function( elem ) {
- var result = Sizzle.attr( elem, name );
-
- if ( result == null ) {
- return operator === "!=";
- }
- if ( !operator ) {
- return true;
- }
-
- result += "";
-
- return operator === "=" ? result === check :
- operator === "!=" ? result !== check :
- operator === "^=" ? check && result.indexOf( check ) === 0 :
- operator === "*=" ? check && result.indexOf( check ) > -1 :
- operator === "$=" ? check && result.slice( -check.length ) === check :
- operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 :
- operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" :
- false;
- };
- },
-
- "CHILD": function( type, what, argument, first, last ) {
- var simple = type.slice( 0, 3 ) !== "nth",
- forward = type.slice( -4 ) !== "last",
- ofType = what === "of-type";
-
- return first === 1 && last === 0 ?
-
- // Shortcut for :nth-*(n)
- function( elem ) {
- return !!elem.parentNode;
- } :
-
- function( elem, context, xml ) {
- var cache, outerCache, node, diff, nodeIndex, start,
- dir = simple !== forward ? "nextSibling" : "previousSibling",
- parent = elem.parentNode,
- name = ofType && elem.nodeName.toLowerCase(),
- useCache = !xml && !ofType;
-
- if ( parent ) {
-
- // :(first|last|only)-(child|of-type)
- if ( simple ) {
- while ( dir ) {
- node = elem;
- while ( (node = node[ dir ]) ) {
- if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) {
- return false;
- }
- }
- // Reverse direction for :only-* (if we haven't yet done so)
- start = dir = type === "only" && !start && "nextSibling";
- }
- return true;
- }
-
- start = [ forward ? parent.firstChild : parent.lastChild ];
-
- // non-xml :nth-child(...) stores cache data on `parent`
- if ( forward && useCache ) {
- // Seek `elem` from a previously-cached index
- outerCache = parent[ expando ] || (parent[ expando ] = {});
- cache = outerCache[ type ] || [];
- nodeIndex = cache[0] === dirruns && cache[1];
- diff = cache[0] === dirruns && cache[2];
- node = nodeIndex && parent.childNodes[ nodeIndex ];
-
- while ( (node = ++nodeIndex && node && node[ dir ] ||
-
- // Fallback to seeking `elem` from the start
- (diff = nodeIndex = 0) || start.pop()) ) {
-
- // When found, cache indexes on `parent` and break
- if ( node.nodeType === 1 && ++diff && node === elem ) {
- outerCache[ type ] = [ dirruns, nodeIndex, diff ];
- break;
- }
- }
-
- // Use previously-cached element index if available
- } else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) {
- diff = cache[1];
-
- // xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...)
- } else {
- // Use the same loop as above to seek `elem` from the start
- while ( (node = ++nodeIndex && node && node[ dir ] ||
- (diff = nodeIndex = 0) || start.pop()) ) {
-
- if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) {
- // Cache the index of each encountered element
- if ( useCache ) {
- (node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ];
- }
-
- if ( node === elem ) {
- break;
- }
- }
- }
- }
-
- // Incorporate the offset, then check against cycle size
- diff -= last;
- return diff === first || ( diff % first === 0 && diff / first >= 0 );
- }
- };
- },
-
- "PSEUDO": function( pseudo, argument ) {
- // pseudo-class names are case-insensitive
- // http://www.w3.org/TR/selectors/#pseudo-classes
- // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
- // Remember that setFilters inherits from pseudos
- var args,
- fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||
- Sizzle.error( "unsupported pseudo: " + pseudo );
-
- // The user may use createPseudo to indicate that
- // arguments are needed to create the filter function
- // just as Sizzle does
- if ( fn[ expando ] ) {
- return fn( argument );
- }
-
- // But maintain support for old signatures
- if ( fn.length > 1 ) {
- args = [ pseudo, pseudo, "", argument ];
- return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?
- markFunction(function( seed, matches ) {
- var idx,
- matched = fn( seed, argument ),
- i = matched.length;
- while ( i-- ) {
- idx = indexOf.call( seed, matched[i] );
- seed[ idx ] = !( matches[ idx ] = matched[i] );
- }
- }) :
- function( elem ) {
- return fn( elem, 0, args );
- };
- }
-
- return fn;
- }
- },
-
- pseudos: {
- // Potentially complex pseudos
- "not": markFunction(function( selector ) {
- // Trim the selector passed to compile
- // to avoid treating leading and trailing
- // spaces as combinators
- var input = [],
- results = [],
- matcher = compile( selector.replace( rtrim, "$1" ) );
-
- return matcher[ expando ] ?
- markFunction(function( seed, matches, context, xml ) {
- var elem,
- unmatched = matcher( seed, null, xml, [] ),
- i = seed.length;
-
- // Match elements unmatched by `matcher`
- while ( i-- ) {
- if ( (elem = unmatched[i]) ) {
- seed[i] = !(matches[i] = elem);
- }
- }
- }) :
- function( elem, context, xml ) {
- input[0] = elem;
- matcher( input, null, xml, results );
- return !results.pop();
- };
- }),
-
- "has": markFunction(function( selector ) {
- return function( elem ) {
- return Sizzle( selector, elem ).length > 0;
- };
- }),
-
- "contains": markFunction(function( text ) {
- return function( elem ) {
- return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1;
- };
- }),
-
- // "Whether an element is represented by a :lang() selector
- // is based solely on the element's language value
- // being equal to the identifier C,
- // or beginning with the identifier C immediately followed by "-".
- // The matching of C against the element's language value is performed case-insensitively.
- // The identifier C does not have to be a valid language name."
- // http://www.w3.org/TR/selectors/#lang-pseudo
- "lang": markFunction( function( lang ) {
- // lang value must be a valid identifier
- if ( !ridentifier.test(lang || "") ) {
- Sizzle.error( "unsupported lang: " + lang );
- }
- lang = lang.replace( runescape, funescape ).toLowerCase();
- return function( elem ) {
- var elemLang;
- do {
- if ( (elemLang = documentIsHTML ?
- elem.lang :
- elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) {
-
- elemLang = elemLang.toLowerCase();
- return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0;
- }
- } while ( (elem = elem.parentNode) && elem.nodeType === 1 );
- return false;
- };
- }),
-
- // Miscellaneous
- "target": function( elem ) {
- var hash = window.location && window.location.hash;
- return hash && hash.slice( 1 ) === elem.id;
- },
-
- "root": function( elem ) {
- return elem === docElem;
- },
-
- "focus": function( elem ) {
- return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
- },
-
- // Boolean properties
- "enabled": function( elem ) {
- return elem.disabled === false;
- },
-
- "disabled": function( elem ) {
- return elem.disabled === true;
- },
-
- "checked": function( elem ) {
- // In CSS3, :checked should return both checked and selected elements
- // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
- var nodeName = elem.nodeName.toLowerCase();
- return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected);
- },
-
- "selected": function( elem ) {
- // Accessing this property makes selected-by-default
- // options in Safari work properly
- if ( elem.parentNode ) {
- elem.parentNode.selectedIndex;
- }
-
- return elem.selected === true;
- },
-
- // Contents
- "empty": function( elem ) {
- // http://www.w3.org/TR/selectors/#empty-pseudo
- // :empty is only affected by element nodes and content nodes(including text(3), cdata(4)),
- // not comment, processing instructions, or others
- // Thanks to Diego Perini for the nodeName shortcut
- // Greater than "@" means alpha characters (specifically not starting with "#" or "?")
- for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
- if ( elem.nodeName > "@" || elem.nodeType === 3 || elem.nodeType === 4 ) {
- return false;
- }
- }
- return true;
- },
-
- "parent": function( elem ) {
- return !Expr.pseudos["empty"]( elem );
- },
-
- // Element/input types
- "header": function( elem ) {
- return rheader.test( elem.nodeName );
- },
-
- "input": function( elem ) {
- return rinputs.test( elem.nodeName );
- },
-
- "button": function( elem ) {
- var name = elem.nodeName.toLowerCase();
- return name === "input" && elem.type === "button" || name === "button";
- },
-
- "text": function( elem ) {
- var attr;
- // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc)
- // use getAttribute instead to test this case
- return elem.nodeName.toLowerCase() === "input" &&
- elem.type === "text" &&
- ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === elem.type );
- },
-
- // Position-in-collection
- "first": createPositionalPseudo(function() {
- return [ 0 ];
- }),
-
- "last": createPositionalPseudo(function( matchIndexes, length ) {
- return [ length - 1 ];
- }),
-
- "eq": createPositionalPseudo(function( matchIndexes, length, argument ) {
- return [ argument < 0 ? argument + length : argument ];
- }),
-
- "even": createPositionalPseudo(function( matchIndexes, length ) {
- var i = 0;
- for ( ; i < length; i += 2 ) {
- matchIndexes.push( i );
- }
- return matchIndexes;
- }),
-
- "odd": createPositionalPseudo(function( matchIndexes, length ) {
- var i = 1;
- for ( ; i < length; i += 2 ) {
- matchIndexes.push( i );
- }
- return matchIndexes;
- }),
-
- "lt": createPositionalPseudo(function( matchIndexes, length, argument ) {
- var i = argument < 0 ? argument + length : argument;
- for ( ; --i >= 0; ) {
- matchIndexes.push( i );
- }
- return matchIndexes;
- }),
-
- "gt": createPositionalPseudo(function( matchIndexes, length, argument ) {
- var i = argument < 0 ? argument + length : argument;
- for ( ; ++i < length; ) {
- matchIndexes.push( i );
- }
- return matchIndexes;
- })
- }
+ // Can be adjusted by the user
+ cacheLength: 50,
+
+ createPseudo: markFunction,
+
+ match: matchExpr,
+
+ attrHandle: {},
+
+ find: {},
+
+ relative: {
+ ">": { dir: "parentNode", first: true },
+ " ": { dir: "parentNode" },
+ "+": { dir: "previousSibling", first: true },
+ "~": { dir: "previousSibling" }
+ },
+
+ preFilter: {
+ "ATTR": function( match ) {
+ match[1] = match[1].replace( runescape, funescape );
+
+ // Move the given value to match[3] whether quoted or unquoted
+ match[3] = ( match[4] || match[5] || "" ).replace( runescape, funescape );
+
+ if ( match[2] === "~=" ) {
+ match[3] = " " + match[3] + " ";
+ }
+
+ return match.slice( 0, 4 );
+ },
+
+ "CHILD": function( match ) {
+ /* matches from matchExpr["CHILD"]
+ 1 type (only|nth|...)
+ 2 what (child|of-type)
+ 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
+ 4 xn-component of xn+y argument ([+-]?\d*n|)
+ 5 sign of xn-component
+ 6 x of xn-component
+ 7 sign of y-component
+ 8 y of y-component
+ */
+ match[1] = match[1].toLowerCase();
+
+ if ( match[1].slice( 0, 3 ) === "nth" ) {
+ // nth-* requires argument
+ if ( !match[3] ) {
+ Sizzle.error( match[0] );
+ }
+
+ // numeric x and y parameters for Expr.filter.CHILD
+ // remember that false/true cast respectively to 0/1
+ match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) );
+ match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" );
+
+ // other types prohibit arguments
+ } else if ( match[3] ) {
+ Sizzle.error( match[0] );
+ }
+
+ return match;
+ },
+
+ "PSEUDO": function( match ) {
+ var excess,
+ unquoted = !match[5] && match[2];
+
+ if ( matchExpr["CHILD"].test( match[0] ) ) {
+ return null;
+ }
+
+ // Accept quoted arguments as-is
+ if ( match[3] && match[4] !== undefined ) {
+ match[2] = match[4];
+
+ // Strip excess characters from unquoted arguments
+ } else if ( unquoted && rpseudo.test( unquoted ) &&
+ // Get excess from tokenize (recursively)
+ (excess = tokenize( unquoted, true )) &&
+ // advance to the next closing parenthesis
+ (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) {
+
+ // excess is a negative index
+ match[0] = match[0].slice( 0, excess );
+ match[2] = unquoted.slice( 0, excess );
+ }
+
+ // Return only captures needed by the pseudo filter method (type and argument)
+ return match.slice( 0, 3 );
+ }
+ },
+
+ filter: {
+
+ "TAG": function( nodeNameSelector ) {
+ var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase();
+ return nodeNameSelector === "*" ?
+ function() { return true; } :
+ function( elem ) {
+ return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
+ };
+ },
+
+ "CLASS": function( className ) {
+ var pattern = classCache[ className + " " ];
+
+ return pattern ||
+ (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) &&
+ classCache( className, function( elem ) {
+ return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== strundefined && elem.getAttribute("class") || "" );
+ });
+ },
+
+ "ATTR": function( name, operator, check ) {
+ return function( elem ) {
+ var result = Sizzle.attr( elem, name );
+
+ if ( result == null ) {
+ return operator === "!=";
+ }
+ if ( !operator ) {
+ return true;
+ }
+
+ result += "";
+
+ return operator === "=" ? result === check :
+ operator === "!=" ? result !== check :
+ operator === "^=" ? check && result.indexOf( check ) === 0 :
+ operator === "*=" ? check && result.indexOf( check ) > -1 :
+ operator === "$=" ? check && result.slice( -check.length ) === check :
+ operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 :
+ operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" :
+ false;
+ };
+ },
+
+ "CHILD": function( type, what, argument, first, last ) {
+ var simple = type.slice( 0, 3 ) !== "nth",
+ forward = type.slice( -4 ) !== "last",
+ ofType = what === "of-type";
+
+ return first === 1 && last === 0 ?
+
+ // Shortcut for :nth-*(n)
+ function( elem ) {
+ return !!elem.parentNode;
+ } :
+
+ function( elem, context, xml ) {
+ var cache, outerCache, node, diff, nodeIndex, start,
+ dir = simple !== forward ? "nextSibling" : "previousSibling",
+ parent = elem.parentNode,
+ name = ofType && elem.nodeName.toLowerCase(),
+ useCache = !xml && !ofType;
+
+ if ( parent ) {
+
+ // :(first|last|only)-(child|of-type)
+ if ( simple ) {
+ while ( dir ) {
+ node = elem;
+ while ( (node = node[ dir ]) ) {
+ if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) {
+ return false;
+ }
+ }
+ // Reverse direction for :only-* (if we haven't yet done so)
+ start = dir = type === "only" && !start && "nextSibling";
+ }
+ return true;
+ }
+
+ start = [ forward ? parent.firstChild : parent.lastChild ];
+
+ // non-xml :nth-child(...) stores cache data on `parent`
+ if ( forward && useCache ) {
+ // Seek `elem` from a previously-cached index
+ outerCache = parent[ expando ] || (parent[ expando ] = {});
+ cache = outerCache[ type ] || [];
+ nodeIndex = cache[0] === dirruns && cache[1];
+ diff = cache[0] === dirruns && cache[2];
+ node = nodeIndex && parent.childNodes[ nodeIndex ];
+
+ while ( (node = ++nodeIndex && node && node[ dir ] ||
+
+ // Fallback to seeking `elem` from the start
+ (diff = nodeIndex = 0) || start.pop()) ) {
+
+ // When found, cache indexes on `parent` and break
+ if ( node.nodeType === 1 && ++diff && node === elem ) {
+ outerCache[ type ] = [ dirruns, nodeIndex, diff ];
+ break;
+ }
+ }
+
+ // Use previously-cached element index if available
+ } else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) {
+ diff = cache[1];
+
+ // xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...)
+ } else {
+ // Use the same loop as above to seek `elem` from the start
+ while ( (node = ++nodeIndex && node && node[ dir ] ||
+ (diff = nodeIndex = 0) || start.pop()) ) {
+
+ if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) {
+ // Cache the index of each encountered element
+ if ( useCache ) {
+ (node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ];
+ }
+
+ if ( node === elem ) {
+ break;
+ }
+ }
+ }
+ }
+
+ // Incorporate the offset, then check against cycle size
+ diff -= last;
+ return diff === first || ( diff % first === 0 && diff / first >= 0 );
+ }
+ };
+ },
+
+ "PSEUDO": function( pseudo, argument ) {
+ // pseudo-class names are case-insensitive
+ // http://www.w3.org/TR/selectors/#pseudo-classes
+ // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
+ // Remember that setFilters inherits from pseudos
+ var args,
+ fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||
+ Sizzle.error( "unsupported pseudo: " + pseudo );
+
+ // The user may use createPseudo to indicate that
+ // arguments are needed to create the filter function
+ // just as Sizzle does
+ if ( fn[ expando ] ) {
+ return fn( argument );
+ }
+
+ // But maintain support for old signatures
+ if ( fn.length > 1 ) {
+ args = [ pseudo, pseudo, "", argument ];
+ return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?
+ markFunction(function( seed, matches ) {
+ var idx,
+ matched = fn( seed, argument ),
+ i = matched.length;
+ while ( i-- ) {
+ idx = indexOf.call( seed, matched[i] );
+ seed[ idx ] = !( matches[ idx ] = matched[i] );
+ }
+ }) :
+ function( elem ) {
+ return fn( elem, 0, args );
+ };
+ }
+
+ return fn;
+ }
+ },
+
+ pseudos: {
+ // Potentially complex pseudos
+ "not": markFunction(function( selector ) {
+ // Trim the selector passed to compile
+ // to avoid treating leading and trailing
+ // spaces as combinators
+ var input = [],
+ results = [],
+ matcher = compile( selector.replace( rtrim, "$1" ) );
+
+ return matcher[ expando ] ?
+ markFunction(function( seed, matches, context, xml ) {
+ var elem,
+ unmatched = matcher( seed, null, xml, [] ),
+ i = seed.length;
+
+ // Match elements unmatched by `matcher`
+ while ( i-- ) {
+ if ( (elem = unmatched[i]) ) {
+ seed[i] = !(matches[i] = elem);
+ }
+ }
+ }) :
+ function( elem, context, xml ) {
+ input[0] = elem;
+ matcher( input, null, xml, results );
+ return !results.pop();
+ };
+ }),
+
+ "has": markFunction(function( selector ) {
+ return function( elem ) {
+ return Sizzle( selector, elem ).length > 0;
+ };
+ }),
+
+ "contains": markFunction(function( text ) {
+ return function( elem ) {
+ return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1;
+ };
+ }),
+
+ // "Whether an element is represented by a :lang() selector
+ // is based solely on the element's language value
+ // being equal to the identifier C,
+ // or beginning with the identifier C immediately followed by "-".
+ // The matching of C against the element's language value is performed case-insensitively.
+ // The identifier C does not have to be a valid language name."
+ // http://www.w3.org/TR/selectors/#lang-pseudo
+ "lang": markFunction( function( lang ) {
+ // lang value must be a valid identifier
+ if ( !ridentifier.test(lang || "") ) {
+ Sizzle.error( "unsupported lang: " + lang );
+ }
+ lang = lang.replace( runescape, funescape ).toLowerCase();
+ return function( elem ) {
+ var elemLang;
+ do {
+ if ( (elemLang = documentIsHTML ?
+ elem.lang :
+ elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) {
+
+ elemLang = elemLang.toLowerCase();
+ return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0;
+ }
+ } while ( (elem = elem.parentNode) && elem.nodeType === 1 );
+ return false;
+ };
+ }),
+
+ // Miscellaneous
+ "target": function( elem ) {
+ var hash = window.location && window.location.hash;
+ return hash && hash.slice( 1 ) === elem.id;
+ },
+
+ "root": function( elem ) {
+ return elem === docElem;
+ },
+
+ "focus": function( elem ) {
+ return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
+ },
+
+ // Boolean properties
+ "enabled": function( elem ) {
+ return elem.disabled === false;
+ },
+
+ "disabled": function( elem ) {
+ return elem.disabled === true;
+ },
+
+ "checked": function( elem ) {
+ // In CSS3, :checked should return both checked and selected elements
+ // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
+ var nodeName = elem.nodeName.toLowerCase();
+ return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected);
+ },
+
+ "selected": function( elem ) {
+ // Accessing this property makes selected-by-default
+ // options in Safari work properly
+ if ( elem.parentNode ) {
+ elem.parentNode.selectedIndex;
+ }
+
+ return elem.selected === true;
+ },
+
+ // Contents
+ "empty": function( elem ) {
+ // http://www.w3.org/TR/selectors/#empty-pseudo
+ // :empty is only affected by element nodes and content nodes(including text(3), cdata(4)),
+ // not comment, processing instructions, or others
+ // Thanks to Diego Perini for the nodeName shortcut
+ // Greater than "@" means alpha characters (specifically not starting with "#" or "?")
+ for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
+ if ( elem.nodeName > "@" || elem.nodeType === 3 || elem.nodeType === 4 ) {
+ return false;
+ }
+ }
+ return true;
+ },
+
+ "parent": function( elem ) {
+ return !Expr.pseudos["empty"]( elem );
+ },
+
+ // Element/input types
+ "header": function( elem ) {
+ return rheader.test( elem.nodeName );
+ },
+
+ "input": function( elem ) {
+ return rinputs.test( elem.nodeName );
+ },
+
+ "button": function( elem ) {
+ var name = elem.nodeName.toLowerCase();
+ return name === "input" && elem.type === "button" || name === "button";
+ },
+
+ "text": function( elem ) {
+ var attr;
+ // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc)
+ // use getAttribute instead to test this case
+ return elem.nodeName.toLowerCase() === "input" &&
+ elem.type === "text" &&
+ ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === elem.type );
+ },
+
+ // Position-in-collection
+ "first": createPositionalPseudo(function() {
+ return [ 0 ];
+ }),
+
+ "last": createPositionalPseudo(function( matchIndexes, length ) {
+ return [ length - 1 ];
+ }),
+
+ "eq": createPositionalPseudo(function( matchIndexes, length, argument ) {
+ return [ argument < 0 ? argument + length : argument ];
+ }),
+
+ "even": createPositionalPseudo(function( matchIndexes, length ) {
+ var i = 0;
+ for ( ; i < length; i += 2 ) {
+ matchIndexes.push( i );
+ }
+ return matchIndexes;
+ }),
+
+ "odd": createPositionalPseudo(function( matchIndexes, length ) {
+ var i = 1;
+ for ( ; i < length; i += 2 ) {
+ matchIndexes.push( i );
+ }
+ return matchIndexes;
+ }),
+
+ "lt": createPositionalPseudo(function( matchIndexes, length, argument ) {
+ var i = argument < 0 ? argument + length : argument;
+ for ( ; --i >= 0; ) {
+ matchIndexes.push( i );
+ }
+ return matchIndexes;
+ }),
+
+ "gt": createPositionalPseudo(function( matchIndexes, length, argument ) {
+ var i = argument < 0 ? argument + length : argument;
+ for ( ; ++i < length; ) {
+ matchIndexes.push( i );
+ }
+ return matchIndexes;
+ })
+ }
};
// Add button/input type pseudos
for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) {
- Expr.pseudos[ i ] = createInputPseudo( i );
+ Expr.pseudos[ i ] = createInputPseudo( i );
}
for ( i in { submit: true, reset: true } ) {
- Expr.pseudos[ i ] = createButtonPseudo( i );
+ Expr.pseudos[ i ] = createButtonPseudo( i );
}
function tokenize( selector, parseOnly ) {
- var matched, match, tokens, type,
- soFar, groups, preFilters,
- cached = tokenCache[ selector + " " ];
+ var matched, match, tokens, type,
+ soFar, groups, preFilters,
+ cached = tokenCache[ selector + " " ];
- if ( cached ) {
- return parseOnly ? 0 : cached.slice( 0 );
- }
+ if ( cached ) {
+ return parseOnly ? 0 : cached.slice( 0 );
+ }
- soFar = selector;
- groups = [];
- preFilters = Expr.preFilter;
+ soFar = selector;
+ groups = [];
+ preFilters = Expr.preFilter;
- while ( soFar ) {
+ while ( soFar ) {
- // Comma and first run
- if ( !matched || (match = rcomma.exec( soFar )) ) {
- if ( match ) {
- // Don't consume trailing commas as valid
- soFar = soFar.slice( match[0].length ) || soFar;
- }
- groups.push( tokens = [] );
- }
+ // Comma and first run
+ if ( !matched || (match = rcomma.exec( soFar )) ) {
+ if ( match ) {
+ // Don't consume trailing commas as valid
+ soFar = soFar.slice( match[0].length ) || soFar;
+ }
+ groups.push( tokens = [] );
+ }
- matched = false;
+ matched = false;
- // Combinators
- if ( (match = rcombinators.exec( soFar )) ) {
- matched = match.shift();
- tokens.push({
- value: matched,
- // Cast descendant combinators to space
- type: match[0].replace( rtrim, " " )
- });
- soFar = soFar.slice( matched.length );
- }
+ // Combinators
+ if ( (match = rcombinators.exec( soFar )) ) {
+ matched = match.shift();
+ tokens.push({
+ value: matched,
+ // Cast descendant combinators to space
+ type: match[0].replace( rtrim, " " )
+ });
+ soFar = soFar.slice( matched.length );
+ }
- // Filters
- for ( type in Expr.filter ) {
- if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] ||
- (match = preFilters[ type ]( match ))) ) {
- matched = match.shift();
- tokens.push({
- value: matched,
- type: type,
- matches: match
- });
- soFar = soFar.slice( matched.length );
- }
- }
+ // Filters
+ for ( type in Expr.filter ) {
+ if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] ||
+ (match = preFilters[ type ]( match ))) ) {
+ matched = match.shift();
+ tokens.push({
+ value: matched,
+ type: type,
+ matches: match
+ });
+ soFar = soFar.slice( matched.length );
+ }
+ }
- if ( !matched ) {
- break;
- }
- }
+ if ( !matched ) {
+ break;
+ }
+ }
- // Return the length of the invalid excess
- // if we're just parsing
- // Otherwise, throw an error or return tokens
- return parseOnly ?
- soFar.length :
- soFar ?
- Sizzle.error( selector ) :
- // Cache the tokens
- tokenCache( selector, groups ).slice( 0 );
+ // Return the length of the invalid excess
+ // if we're just parsing
+ // Otherwise, throw an error or return tokens
+ return parseOnly ?
+ soFar.length :
+ soFar ?
+ Sizzle.error( selector ) :
+ // Cache the tokens
+ tokenCache( selector, groups ).slice( 0 );
}
function toSelector( tokens ) {
- var i = 0,
- len = tokens.length,
- selector = "";
- for ( ; i < len; i++ ) {
- selector += tokens[i].value;
- }
- return selector;
+ var i = 0,
+ len = tokens.length,
+ selector = "";
+ for ( ; i < len; i++ ) {
+ selector += tokens[i].value;
+ }
+ return selector;
}
function addCombinator( matcher, combinator, base ) {
- var dir = combinator.dir,
- checkNonElements = base && dir === "parentNode",
- doneName = done++;
+ var dir = combinator.dir,
+ checkNonElements = base && dir === "parentNode",
+ doneName = done++;
- return combinator.first ?
- // Check against closest ancestor/preceding element
- function( elem, context, xml ) {
- while ( (elem = elem[ dir ]) ) {
- if ( elem.nodeType === 1 || checkNonElements ) {
- return matcher( elem, context, xml );
- }
- }
- } :
+ return combinator.first ?
+ // Check against closest ancestor/preceding element
+ function( elem, context, xml ) {
+ while ( (elem = elem[ dir ]) ) {
+ if ( elem.nodeType === 1 || checkNonElements ) {
+ return matcher( elem, context, xml );
+ }
+ }
+ } :
- // Check against all ancestor/preceding elements
- function( elem, context, xml ) {
- var data, cache, outerCache,
- dirkey = dirruns + " " + doneName;
+ // Check against all ancestor/preceding elements
+ function( elem, context, xml ) {
+ var data, cache, outerCache,
+ dirkey = dirruns + " " + doneName;
- // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching
- if ( xml ) {
- while ( (elem = elem[ dir ]) ) {
- if ( elem.nodeType === 1 || checkNonElements ) {
- if ( matcher( elem, context, xml ) ) {
- return true;
- }
- }
- }
- } else {
- while ( (elem = elem[ dir ]) ) {
- if ( elem.nodeType === 1 || checkNonElements ) {
- outerCache = elem[ expando ] || (elem[ expando ] = {});
- if ( (cache = outerCache[ dir ]) && cache[0] === dirkey ) {
- if ( (data = cache[1]) === true || data === cachedruns ) {
- return data === true;
- }
- } else {
- cache = outerCache[ dir ] = [ dirkey ];
- cache[1] = matcher( elem, context, xml ) || cachedruns;
- if ( cache[1] === true ) {
- return true;
- }
- }
- }
- }
- }
- };
+ // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching
+ if ( xml ) {
+ while ( (elem = elem[ dir ]) ) {
+ if ( elem.nodeType === 1 || checkNonElements ) {
+ if ( matcher( elem, context, xml ) ) {
+ return true;
+ }
+ }
+ }
+ } else {
+ while ( (elem = elem[ dir ]) ) {
+ if ( elem.nodeType === 1 || checkNonElements ) {
+ outerCache = elem[ expando ] || (elem[ expando ] = {});
+ if ( (cache = outerCache[ dir ]) && cache[0] === dirkey ) {
+ if ( (data = cache[1]) === true || data === cachedruns ) {
+ return data === true;
+ }
+ } else {
+ cache = outerCache[ dir ] = [ dirkey ];
+ cache[1] = matcher( elem, context, xml ) || cachedruns;
+ if ( cache[1] === true ) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ };
}
function elementMatcher( matchers ) {
- return matchers.length > 1 ?
- function( elem, context, xml ) {
- var i = matchers.length;
- while ( i-- ) {
- if ( !matchers[i]( elem, context, xml ) ) {
- return false;
- }
- }
- return true;
- } :
- matchers[0];
+ return matchers.length > 1 ?
+ function( elem, context, xml ) {
+ var i = matchers.length;
+ while ( i-- ) {
+ if ( !matchers[i]( elem, context, xml ) ) {
+ return false;
+ }
+ }
+ return true;
+ } :
+ matchers[0];
}
function condense( unmatched, map, filter, context, xml ) {
- var elem,
- newUnmatched = [],
- i = 0,
- len = unmatched.length,
- mapped = map != null;
+ var elem,
+ newUnmatched = [],
+ i = 0,
+ len = unmatched.length,
+ mapped = map != null;
- for ( ; i < len; i++ ) {
- if ( (elem = unmatched[i]) ) {
- if ( !filter || filter( elem, context, xml ) ) {
- newUnmatched.push( elem );
- if ( mapped ) {
- map.push( i );
- }
- }
- }
- }
+ for ( ; i < len; i++ ) {
+ if ( (elem = unmatched[i]) ) {
+ if ( !filter || filter( elem, context, xml ) ) {
+ newUnmatched.push( elem );
+ if ( mapped ) {
+ map.push( i );
+ }
+ }
+ }
+ }
- return newUnmatched;
+ return newUnmatched;
}
function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {
- if ( postFilter && !postFilter[ expando ] ) {
- postFilter = setMatcher( postFilter );
- }
- if ( postFinder && !postFinder[ expando ] ) {
- postFinder = setMatcher( postFinder, postSelector );
- }
- return markFunction(function( seed, results, context, xml ) {
- var temp, i, elem,
- preMap = [],
- postMap = [],
- preexisting = results.length,
+ if ( postFilter && !postFilter[ expando ] ) {
+ postFilter = setMatcher( postFilter );
+ }
+ if ( postFinder && !postFinder[ expando ] ) {
+ postFinder = setMatcher( postFinder, postSelector );
+ }
+ return markFunction(function( seed, results, context, xml ) {
+ var temp, i, elem,
+ preMap = [],
+ postMap = [],
+ preexisting = results.length,
- // Get initial elements from seed or context
- elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ),
+ // Get initial elements from seed or context
+ elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ),
- // Prefilter to get matcher input, preserving a map for seed-results synchronization
- matcherIn = preFilter && ( seed || !selector ) ?
- condense( elems, preMap, preFilter, context, xml ) :
- elems,
+ // Prefilter to get matcher input, preserving a map for seed-results synchronization
+ matcherIn = preFilter && ( seed || !selector ) ?
+ condense( elems, preMap, preFilter, context, xml ) :
+ elems,
- matcherOut = matcher ?
- // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,
- postFinder || ( seed ? preFilter : preexisting || postFilter ) ?
+ matcherOut = matcher ?
+ // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,
+ postFinder || ( seed ? preFilter : preexisting || postFilter ) ?
- // ...intermediate processing is necessary
- [] :
+ // ...intermediate processing is necessary
+ [] :
- // ...otherwise use results directly
- results :
- matcherIn;
+ // ...otherwise use results directly
+ results :
+ matcherIn;
- // Find primary matches
- if ( matcher ) {
- matcher( matcherIn, matcherOut, context, xml );
- }
+ // Find primary matches
+ if ( matcher ) {
+ matcher( matcherIn, matcherOut, context, xml );
+ }
- // Apply postFilter
- if ( postFilter ) {
- temp = condense( matcherOut, postMap );
- postFilter( temp, [], context, xml );
+ // Apply postFilter
+ if ( postFilter ) {
+ temp = condense( matcherOut, postMap );
+ postFilter( temp, [], context, xml );
- // Un-match failing elements by moving them back to matcherIn
- i = temp.length;
- while ( i-- ) {
- if ( (elem = temp[i]) ) {
- matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem);
- }
- }
- }
+ // Un-match failing elements by moving them back to matcherIn
+ i = temp.length;
+ while ( i-- ) {
+ if ( (elem = temp[i]) ) {
+ matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem);
+ }
+ }
+ }
- if ( seed ) {
- if ( postFinder || preFilter ) {
- if ( postFinder ) {
- // Get the final matcherOut by condensing this intermediate into postFinder contexts
- temp = [];
- i = matcherOut.length;
- while ( i-- ) {
- if ( (elem = matcherOut[i]) ) {
- // Restore matcherIn since elem is not yet a final match
- temp.push( (matcherIn[i] = elem) );
- }
- }
- postFinder( null, (matcherOut = []), temp, xml );
- }
+ if ( seed ) {
+ if ( postFinder || preFilter ) {
+ if ( postFinder ) {
+ // Get the final matcherOut by condensing this intermediate into postFinder contexts
+ temp = [];
+ i = matcherOut.length;
+ while ( i-- ) {
+ if ( (elem = matcherOut[i]) ) {
+ // Restore matcherIn since elem is not yet a final match
+ temp.push( (matcherIn[i] = elem) );
+ }
+ }
+ postFinder( null, (matcherOut = []), temp, xml );
+ }
- // Move matched elements from seed to results to keep them synchronized
- i = matcherOut.length;
- while ( i-- ) {
- if ( (elem = matcherOut[i]) &&
- (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) {
+ // Move matched elements from seed to results to keep them synchronized
+ i = matcherOut.length;
+ while ( i-- ) {
+ if ( (elem = matcherOut[i]) &&
+ (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) {
- seed[temp] = !(results[temp] = elem);
- }
- }
- }
+ seed[temp] = !(results[temp] = elem);
+ }
+ }
+ }
- // Add elements to results, through postFinder if defined
- } else {
- matcherOut = condense(
- matcherOut === results ?
- matcherOut.splice( preexisting, matcherOut.length ) :
- matcherOut
- );
- if ( postFinder ) {
- postFinder( null, results, matcherOut, xml );
- } else {
- push.apply( results, matcherOut );
- }
- }
- });
+ // Add elements to results, through postFinder if defined
+ } else {
+ matcherOut = condense(
+ matcherOut === results ?
+ matcherOut.splice( preexisting, matcherOut.length ) :
+ matcherOut
+ );
+ if ( postFinder ) {
+ postFinder( null, results, matcherOut, xml );
+ } else {
+ push.apply( results, matcherOut );
+ }
+ }
+ });
}
function matcherFromTokens( tokens ) {
- var checkContext, matcher, j,
- len = tokens.length,
- leadingRelative = Expr.relative[ tokens[0].type ],
- implicitRelative = leadingRelative || Expr.relative[" "],
- i = leadingRelative ? 1 : 0,
+ var checkContext, matcher, j,
+ len = tokens.length,
+ leadingRelative = Expr.relative[ tokens[0].type ],
+ implicitRelative = leadingRelative || Expr.relative[" "],
+ i = leadingRelative ? 1 : 0,
- // The foundational matcher ensures that elements are reachable from top-level context(s)
- matchContext = addCombinator( function( elem ) {
- return elem === checkContext;
- }, implicitRelative, true ),
- matchAnyContext = addCombinator( function( elem ) {
- return indexOf.call( checkContext, elem ) > -1;
- }, implicitRelative, true ),
- matchers = [ function( elem, context, xml ) {
- return ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
- (checkContext = context).nodeType ?
- matchContext( elem, context, xml ) :
- matchAnyContext( elem, context, xml ) );
- } ];
+ // The foundational matcher ensures that elements are reachable from top-level context(s)
+ matchContext = addCombinator( function( elem ) {
+ return elem === checkContext;
+ }, implicitRelative, true ),
+ matchAnyContext = addCombinator( function( elem ) {
+ return indexOf.call( checkContext, elem ) > -1;
+ }, implicitRelative, true ),
+ matchers = [ function( elem, context, xml ) {
+ return ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
+ (checkContext = context).nodeType ?
+ matchContext( elem, context, xml ) :
+ matchAnyContext( elem, context, xml ) );
+ } ];
- for ( ; i < len; i++ ) {
- if ( (matcher = Expr.relative[ tokens[i].type ]) ) {
- matchers = [ addCombinator(elementMatcher( matchers ), matcher) ];
- } else {
- matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches );
+ for ( ; i < len; i++ ) {
+ if ( (matcher = Expr.relative[ tokens[i].type ]) ) {
+ matchers = [ addCombinator(elementMatcher( matchers ), matcher) ];
+ } else {
+ matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches );
- // Return special upon seeing a positional matcher
- if ( matcher[ expando ] ) {
- // Find the next relative operator (if any) for proper handling
- j = ++i;
- for ( ; j < len; j++ ) {
- if ( Expr.relative[ tokens[j].type ] ) {
- break;
- }
- }
- return setMatcher(
- i > 1 && elementMatcher( matchers ),
- i > 1 && toSelector(
- // If the preceding token was a descendant combinator, insert an implicit any-element `*`
- tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" })
- ).replace( rtrim, "$1" ),
- matcher,
- i < j && matcherFromTokens( tokens.slice( i, j ) ),
- j < len && matcherFromTokens( (tokens = tokens.slice( j )) ),
- j < len && toSelector( tokens )
- );
- }
- matchers.push( matcher );
- }
- }
+ // Return special upon seeing a positional matcher
+ if ( matcher[ expando ] ) {
+ // Find the next relative operator (if any) for proper handling
+ j = ++i;
+ for ( ; j < len; j++ ) {
+ if ( Expr.relative[ tokens[j].type ] ) {
+ break;
+ }
+ }
+ return setMatcher(
+ i > 1 && elementMatcher( matchers ),
+ i > 1 && toSelector(
+ // If the preceding token was a descendant combinator, insert an implicit any-element `*`
+ tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" })
+ ).replace( rtrim, "$1" ),
+ matcher,
+ i < j && matcherFromTokens( tokens.slice( i, j ) ),
+ j < len && matcherFromTokens( (tokens = tokens.slice( j )) ),
+ j < len && toSelector( tokens )
+ );
+ }
+ matchers.push( matcher );
+ }
+ }
- return elementMatcher( matchers );
+ return elementMatcher( matchers );
}
function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
- // A counter to specify which element is currently being matched
- var matcherCachedRuns = 0,
- bySet = setMatchers.length > 0,
- byElement = elementMatchers.length > 0,
- superMatcher = function( seed, context, xml, results, expandContext ) {
- var elem, j, matcher,
- setMatched = [],
- matchedCount = 0,
- i = "0",
- unmatched = seed && [],
- outermost = expandContext != null,
- contextBackup = outermostContext,
- // We must always have either seed elements or context
- elems = seed || byElement && Expr.find["TAG"]( "*", expandContext && context.parentNode || context ),
- // Use integer dirruns iff this is the outermost matcher
- dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1);
+ // A counter to specify which element is currently being matched
+ var matcherCachedRuns = 0,
+ bySet = setMatchers.length > 0,
+ byElement = elementMatchers.length > 0,
+ superMatcher = function( seed, context, xml, results, expandContext ) {
+ var elem, j, matcher,
+ setMatched = [],
+ matchedCount = 0,
+ i = "0",
+ unmatched = seed && [],
+ outermost = expandContext != null,
+ contextBackup = outermostContext,
+ // We must always have either seed elements or context
+ elems = seed || byElement && Expr.find["TAG"]( "*", expandContext && context.parentNode || context ),
+ // Use integer dirruns iff this is the outermost matcher
+ dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1);
- if ( outermost ) {
- outermostContext = context !== document && context;
- cachedruns = matcherCachedRuns;
- }
+ if ( outermost ) {
+ outermostContext = context !== document && context;
+ cachedruns = matcherCachedRuns;
+ }
- // Add elements passing elementMatchers directly to results
- // Keep `i` a string if there are no elements so `matchedCount` will be "00" below
- for ( ; (elem = elems[i]) != null; i++ ) {
- if ( byElement && elem ) {
- j = 0;
- while ( (matcher = elementMatchers[j++]) ) {
- if ( matcher( elem, context, xml ) ) {
- results.push( elem );
- break;
- }
- }
- if ( outermost ) {
- dirruns = dirrunsUnique;
- cachedruns = ++matcherCachedRuns;
- }
- }
+ // Add elements passing elementMatchers directly to results
+ // Keep `i` a string if there are no elements so `matchedCount` will be "00" below
+ for ( ; (elem = elems[i]) != null; i++ ) {
+ if ( byElement && elem ) {
+ j = 0;
+ while ( (matcher = elementMatchers[j++]) ) {
+ if ( matcher( elem, context, xml ) ) {
+ results.push( elem );
+ break;
+ }
+ }
+ if ( outermost ) {
+ dirruns = dirrunsUnique;
+ cachedruns = ++matcherCachedRuns;
+ }
+ }
- // Track unmatched elements for set filters
- if ( bySet ) {
- // They will have gone through all possible matchers
- if ( (elem = !matcher && elem) ) {
- matchedCount--;
- }
+ // Track unmatched elements for set filters
+ if ( bySet ) {
+ // They will have gone through all possible matchers
+ if ( (elem = !matcher && elem) ) {
+ matchedCount--;
+ }
- // Lengthen the array for every element, matched or not
- if ( seed ) {
- unmatched.push( elem );
- }
- }
- }
+ // Lengthen the array for every element, matched or not
+ if ( seed ) {
+ unmatched.push( elem );
+ }
+ }
+ }
- // Apply set filters to unmatched elements
- matchedCount += i;
- if ( bySet && i !== matchedCount ) {
- j = 0;
- while ( (matcher = setMatchers[j++]) ) {
- matcher( unmatched, setMatched, context, xml );
- }
+ // Apply set filters to unmatched elements
+ matchedCount += i;
+ if ( bySet && i !== matchedCount ) {
+ j = 0;
+ while ( (matcher = setMatchers[j++]) ) {
+ matcher( unmatched, setMatched, context, xml );
+ }
- if ( seed ) {
- // Reintegrate element matches to eliminate the need for sorting
- if ( matchedCount > 0 ) {
- while ( i-- ) {
- if ( !(unmatched[i] || setMatched[i]) ) {
- setMatched[i] = pop.call( results );
- }
- }
- }
+ if ( seed ) {
+ // Reintegrate element matches to eliminate the need for sorting
+ if ( matchedCount > 0 ) {
+ while ( i-- ) {
+ if ( !(unmatched[i] || setMatched[i]) ) {
+ setMatched[i] = pop.call( results );
+ }
+ }
+ }
- // Discard index placeholder values to get only actual matches
- setMatched = condense( setMatched );
- }
+ // Discard index placeholder values to get only actual matches
+ setMatched = condense( setMatched );
+ }
- // Add matches to results
- push.apply( results, setMatched );
+ // Add matches to results
+ push.apply( results, setMatched );
- // Seedless set matches succeeding multiple successful matchers stipulate sorting
- if ( outermost && !seed && setMatched.length > 0 &&
- ( matchedCount + setMatchers.length ) > 1 ) {
+ // Seedless set matches succeeding multiple successful matchers stipulate sorting
+ if ( outermost && !seed && setMatched.length > 0 &&
+ ( matchedCount + setMatchers.length ) > 1 ) {
- Sizzle.uniqueSort( results );
- }
- }
+ Sizzle.uniqueSort( results );
+ }
+ }
- // Override manipulation of globals by nested matchers
- if ( outermost ) {
- dirruns = dirrunsUnique;
- outermostContext = contextBackup;
- }
+ // Override manipulation of globals by nested matchers
+ if ( outermost ) {
+ dirruns = dirrunsUnique;
+ outermostContext = contextBackup;
+ }
- return unmatched;
- };
+ return unmatched;
+ };
- return bySet ?
- markFunction( superMatcher ) :
- superMatcher;
+ return bySet ?
+ markFunction( superMatcher ) :
+ superMatcher;
}
compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) {
- var i,
- setMatchers = [],
- elementMatchers = [],
- cached = compilerCache[ selector + " " ];
+ var i,
+ setMatchers = [],
+ elementMatchers = [],
+ cached = compilerCache[ selector + " " ];
- if ( !cached ) {
- // Generate a function of recursive functions that can be used to check each element
- if ( !group ) {
- group = tokenize( selector );
- }
- i = group.length;
- while ( i-- ) {
- cached = matcherFromTokens( group[i] );
- if ( cached[ expando ] ) {
- setMatchers.push( cached );
- } else {
- elementMatchers.push( cached );
- }
- }
+ if ( !cached ) {
+ // Generate a function of recursive functions that can be used to check each element
+ if ( !group ) {
+ group = tokenize( selector );
+ }
+ i = group.length;
+ while ( i-- ) {
+ cached = matcherFromTokens( group[i] );
+ if ( cached[ expando ] ) {
+ setMatchers.push( cached );
+ } else {
+ elementMatchers.push( cached );
+ }
+ }
- // Cache the compiled function
- cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );
- }
- return cached;
+ // Cache the compiled function
+ cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );
+ }
+ return cached;
};
function multipleContexts( selector, contexts, results ) {
- var i = 0,
- len = contexts.length;
- for ( ; i < len; i++ ) {
- Sizzle( selector, contexts[i], results );
- }
- return results;
+ var i = 0,
+ len = contexts.length;
+ for ( ; i < len; i++ ) {
+ Sizzle( selector, contexts[i], results );
+ }
+ return results;
}
function select( selector, context, results, seed ) {
- var i, tokens, token, type, find,
- match = tokenize( selector );
+ var i, tokens, token, type, find,
+ match = tokenize( selector );
- if ( !seed ) {
- // Try to minimize operations if there is only one group
- if ( match.length === 1 ) {
+ if ( !seed ) {
+ // Try to minimize operations if there is only one group
+ if ( match.length === 1 ) {
- // Take a shortcut and set the context if the root selector is an ID
- tokens = match[0] = match[0].slice( 0 );
- if ( tokens.length > 2 && (token = tokens[0]).type === "ID" &&
- support.getById && context.nodeType === 9 && documentIsHTML &&
- Expr.relative[ tokens[1].type ] ) {
+ // Take a shortcut and set the context if the root selector is an ID
+ tokens = match[0] = match[0].slice( 0 );
+ if ( tokens.length > 2 && (token = tokens[0]).type === "ID" &&
+ support.getById && context.nodeType === 9 && documentIsHTML &&
+ Expr.relative[ tokens[1].type ] ) {
- context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0];
- if ( !context ) {
- return results;
- }
- selector = selector.slice( tokens.shift().value.length );
- }
+ context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0];
+ if ( !context ) {
+ return results;
+ }
+ selector = selector.slice( tokens.shift().value.length );
+ }
- // Fetch a seed set for right-to-left matching
- i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length;
- while ( i-- ) {
- token = tokens[i];
+ // Fetch a seed set for right-to-left matching
+ i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length;
+ while ( i-- ) {
+ token = tokens[i];
- // Abort if we hit a combinator
- if ( Expr.relative[ (type = token.type) ] ) {
- break;
- }
- if ( (find = Expr.find[ type ]) ) {
- // Search, expanding context for leading sibling combinators
- if ( (seed = find(
- token.matches[0].replace( runescape, funescape ),
- rsibling.test( tokens[0].type ) && context.parentNode || context
- )) ) {
+ // Abort if we hit a combinator
+ if ( Expr.relative[ (type = token.type) ] ) {
+ break;
+ }
+ if ( (find = Expr.find[ type ]) ) {
+ // Search, expanding context for leading sibling combinators
+ if ( (seed = find(
+ token.matches[0].replace( runescape, funescape ),
+ rsibling.test( tokens[0].type ) && context.parentNode || context
+ )) ) {
- // If seed is empty or no tokens remain, we can return early
- tokens.splice( i, 1 );
- selector = seed.length && toSelector( tokens );
- if ( !selector ) {
- push.apply( results, seed );
- return results;
- }
+ // If seed is empty or no tokens remain, we can return early
+ tokens.splice( i, 1 );
+ selector = seed.length && toSelector( tokens );
+ if ( !selector ) {
+ push.apply( results, seed );
+ return results;
+ }
- break;
- }
- }
- }
- }
- }
+ break;
+ }
+ }
+ }
+ }
+ }
- // Compile and execute a filtering function
- // Provide `match` to avoid retokenization if we modified the selector above
- compile( selector, match )(
- seed,
- context,
- !documentIsHTML,
- results,
- rsibling.test( selector )
- );
- return results;
+ // Compile and execute a filtering function
+ // Provide `match` to avoid retokenization if we modified the selector above
+ compile( selector, match )(
+ seed,
+ context,
+ !documentIsHTML,
+ results,
+ rsibling.test( selector )
+ );
+ return results;
}
// Deprecated
@@ -2874,643 +2874,643 @@ var optionsCache = {};
// Convert String-formatted options into Object-formatted ones and store in cache
function createOptions( options ) {
- var object = optionsCache[ options ] = {};
- jQuery.each( options.match( core_rnotwhite ) || [], function( _, flag ) {
- object[ flag ] = true;
- });
- return object;
+ var object = optionsCache[ options ] = {};
+ jQuery.each( options.match( core_rnotwhite ) || [], function( _, flag ) {
+ object[ flag ] = true;
+ });
+ return object;
}
/*
* Create a callback list using the following parameters:
*
- * options: an optional list of space-separated options that will change how
- * the callback list behaves or a more traditional option object
+ * options: an optional list of space-separated options that will change how
+ * the callback list behaves or a more traditional option object
*
* By default a callback list will act like an event callback list and can be
* "fired" multiple times.
*
* Possible options:
*
- * once: will ensure the callback list can only be fired once (like a Deferred)
+ * once: will ensure the callback list can only be fired once (like a Deferred)
*
- * memory: will keep track of previous values and will call any callback added
- * after the list has been fired right away with the latest "memorized"
- * values (like a Deferred)
+ * memory: will keep track of previous values and will call any callback added
+ * after the list has been fired right away with the latest "memorized"
+ * values (like a Deferred)
*
- * unique: will ensure a callback can only be added once (no duplicate in the list)
+ * unique: will ensure a callback can only be added once (no duplicate in the list)
*
- * stopOnFalse: interrupt callings when a callback returns false
+ * stopOnFalse: interrupt callings when a callback returns false
*
*/
jQuery.Callbacks = function( options ) {
- // Convert options from String-formatted to Object-formatted if needed
- // (we check in cache first)
- options = typeof options === "string" ?
- ( optionsCache[ options ] || createOptions( options ) ) :
- jQuery.extend( {}, options );
+ // Convert options from String-formatted to Object-formatted if needed
+ // (we check in cache first)
+ options = typeof options === "string" ?
+ ( optionsCache[ options ] || createOptions( options ) ) :
+ jQuery.extend( {}, options );
- var // Last fire value (for non-forgettable lists)
- memory,
- // Flag to know if list was already fired
- fired,
- // Flag to know if list is currently firing
- firing,
- // First callback to fire (used internally by add and fireWith)
- firingStart,
- // End of the loop when firing
- firingLength,
- // Index of currently firing callback (modified by remove if needed)
- firingIndex,
- // Actual callback list
- list = [],
- // Stack of fire calls for repeatable lists
- stack = !options.once && [],
- // Fire callbacks
- fire = function( data ) {
- memory = options.memory && data;
- fired = true;
- firingIndex = firingStart || 0;
- firingStart = 0;
- firingLength = list.length;
- firing = true;
- for ( ; list && firingIndex < firingLength; firingIndex++ ) {
- if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
- memory = false; // To prevent further calls using add
- break;
- }
- }
- firing = false;
- if ( list ) {
- if ( stack ) {
- if ( stack.length ) {
- fire( stack.shift() );
- }
- } else if ( memory ) {
- list = [];
- } else {
- self.disable();
- }
- }
- },
- // Actual Callbacks object
- self = {
- // Add a callback or a collection of callbacks to the list
- add: function() {
- if ( list ) {
- // First, we save the current length
- var start = list.length;
- (function add( args ) {
- jQuery.each( args, function( _, arg ) {
- var type = jQuery.type( arg );
- if ( type === "function" ) {
- if ( !options.unique || !self.has( arg ) ) {
- list.push( arg );
- }
- } else if ( arg && arg.length && type !== "string" ) {
- // Inspect recursively
- add( arg );
- }
- });
- })( arguments );
- // Do we need to add the callbacks to the
- // current firing batch?
- if ( firing ) {
- firingLength = list.length;
- // With memory, if we're not firing then
- // we should call right away
- } else if ( memory ) {
- firingStart = start;
- fire( memory );
- }
- }
- return this;
- },
- // Remove a callback from the list
- remove: function() {
- if ( list ) {
- jQuery.each( arguments, function( _, arg ) {
- var index;
- while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
- list.splice( index, 1 );
- // Handle firing indexes
- if ( firing ) {
- if ( index <= firingLength ) {
- firingLength--;
- }
- if ( index <= firingIndex ) {
- firingIndex--;
- }
- }
- }
- });
- }
- return this;
- },
- // Check if a given callback is in the list.
- // If no argument is given, return whether or not list has callbacks attached.
- has: function( fn ) {
- return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length );
- },
- // Remove all callbacks from the list
- empty: function() {
- list = [];
- firingLength = 0;
- return this;
- },
- // Have the list do nothing anymore
- disable: function() {
- list = stack = memory = undefined;
- return this;
- },
- // Is it disabled?
- disabled: function() {
- return !list;
- },
- // Lock the list in its current state
- lock: function() {
- stack = undefined;
- if ( !memory ) {
- self.disable();
- }
- return this;
- },
- // Is it locked?
- locked: function() {
- return !stack;
- },
- // Call all callbacks with the given context and arguments
- fireWith: function( context, args ) {
- args = args || [];
- args = [ context, args.slice ? args.slice() : args ];
- if ( list && ( !fired || stack ) ) {
- if ( firing ) {
- stack.push( args );
- } else {
- fire( args );
- }
- }
- return this;
- },
- // Call all the callbacks with the given arguments
- fire: function() {
- self.fireWith( this, arguments );
- return this;
- },
- // To know if the callbacks have already been called at least once
- fired: function() {
- return !!fired;
- }
- };
+ var // Last fire value (for non-forgettable lists)
+ memory,
+ // Flag to know if list was already fired
+ fired,
+ // Flag to know if list is currently firing
+ firing,
+ // First callback to fire (used internally by add and fireWith)
+ firingStart,
+ // End of the loop when firing
+ firingLength,
+ // Index of currently firing callback (modified by remove if needed)
+ firingIndex,
+ // Actual callback list
+ list = [],
+ // Stack of fire calls for repeatable lists
+ stack = !options.once && [],
+ // Fire callbacks
+ fire = function( data ) {
+ memory = options.memory && data;
+ fired = true;
+ firingIndex = firingStart || 0;
+ firingStart = 0;
+ firingLength = list.length;
+ firing = true;
+ for ( ; list && firingIndex < firingLength; firingIndex++ ) {
+ if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
+ memory = false; // To prevent further calls using add
+ break;
+ }
+ }
+ firing = false;
+ if ( list ) {
+ if ( stack ) {
+ if ( stack.length ) {
+ fire( stack.shift() );
+ }
+ } else if ( memory ) {
+ list = [];
+ } else {
+ self.disable();
+ }
+ }
+ },
+ // Actual Callbacks object
+ self = {
+ // Add a callback or a collection of callbacks to the list
+ add: function() {
+ if ( list ) {
+ // First, we save the current length
+ var start = list.length;
+ (function add( args ) {
+ jQuery.each( args, function( _, arg ) {
+ var type = jQuery.type( arg );
+ if ( type === "function" ) {
+ if ( !options.unique || !self.has( arg ) ) {
+ list.push( arg );
+ }
+ } else if ( arg && arg.length && type !== "string" ) {
+ // Inspect recursively
+ add( arg );
+ }
+ });
+ })( arguments );
+ // Do we need to add the callbacks to the
+ // current firing batch?
+ if ( firing ) {
+ firingLength = list.length;
+ // With memory, if we're not firing then
+ // we should call right away
+ } else if ( memory ) {
+ firingStart = start;
+ fire( memory );
+ }
+ }
+ return this;
+ },
+ // Remove a callback from the list
+ remove: function() {
+ if ( list ) {
+ jQuery.each( arguments, function( _, arg ) {
+ var index;
+ while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
+ list.splice( index, 1 );
+ // Handle firing indexes
+ if ( firing ) {
+ if ( index <= firingLength ) {
+ firingLength--;
+ }
+ if ( index <= firingIndex ) {
+ firingIndex--;
+ }
+ }
+ }
+ });
+ }
+ return this;
+ },
+ // Check if a given callback is in the list.
+ // If no argument is given, return whether or not list has callbacks attached.
+ has: function( fn ) {
+ return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length );
+ },
+ // Remove all callbacks from the list
+ empty: function() {
+ list = [];
+ firingLength = 0;
+ return this;
+ },
+ // Have the list do nothing anymore
+ disable: function() {
+ list = stack = memory = undefined;
+ return this;
+ },
+ // Is it disabled?
+ disabled: function() {
+ return !list;
+ },
+ // Lock the list in its current state
+ lock: function() {
+ stack = undefined;
+ if ( !memory ) {
+ self.disable();
+ }
+ return this;
+ },
+ // Is it locked?
+ locked: function() {
+ return !stack;
+ },
+ // Call all callbacks with the given context and arguments
+ fireWith: function( context, args ) {
+ args = args || [];
+ args = [ context, args.slice ? args.slice() : args ];
+ if ( list && ( !fired || stack ) ) {
+ if ( firing ) {
+ stack.push( args );
+ } else {
+ fire( args );
+ }
+ }
+ return this;
+ },
+ // Call all the callbacks with the given arguments
+ fire: function() {
+ self.fireWith( this, arguments );
+ return this;
+ },
+ // To know if the callbacks have already been called at least once
+ fired: function() {
+ return !!fired;
+ }
+ };
- return self;
+ return self;
};
jQuery.extend({
- Deferred: function( func ) {
- var tuples = [
- // action, add listener, listener list, final state
- [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
- [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
- [ "notify", "progress", jQuery.Callbacks("memory") ]
- ],
- state = "pending",
- promise = {
- state: function() {
- return state;
- },
- always: function() {
- deferred.done( arguments ).fail( arguments );
- return this;
- },
- then: function( /* fnDone, fnFail, fnProgress */ ) {
- var fns = arguments;
- return jQuery.Deferred(function( newDefer ) {
- jQuery.each( tuples, function( i, tuple ) {
- var action = tuple[ 0 ],
- fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];
- // deferred[ done | fail | progress ] for forwarding actions to newDefer
- deferred[ tuple[1] ](function() {
- var returned = fn && fn.apply( this, arguments );
- if ( returned && jQuery.isFunction( returned.promise ) ) {
- returned.promise()
- .done( newDefer.resolve )
- .fail( newDefer.reject )
- .progress( newDefer.notify );
- } else {
- newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );
- }
- });
- });
- fns = null;
- }).promise();
- },
- // Get a promise for this deferred
- // If obj is provided, the promise aspect is added to the object
- promise: function( obj ) {
- return obj != null ? jQuery.extend( obj, promise ) : promise;
- }
- },
- deferred = {};
+ Deferred: function( func ) {
+ var tuples = [
+ // action, add listener, listener list, final state
+ [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
+ [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
+ [ "notify", "progress", jQuery.Callbacks("memory") ]
+ ],
+ state = "pending",
+ promise = {
+ state: function() {
+ return state;
+ },
+ always: function() {
+ deferred.done( arguments ).fail( arguments );
+ return this;
+ },
+ then: function( /* fnDone, fnFail, fnProgress */ ) {
+ var fns = arguments;
+ return jQuery.Deferred(function( newDefer ) {
+ jQuery.each( tuples, function( i, tuple ) {
+ var action = tuple[ 0 ],
+ fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];
+ // deferred[ done | fail | progress ] for forwarding actions to newDefer
+ deferred[ tuple[1] ](function() {
+ var returned = fn && fn.apply( this, arguments );
+ if ( returned && jQuery.isFunction( returned.promise ) ) {
+ returned.promise()
+ .done( newDefer.resolve )
+ .fail( newDefer.reject )
+ .progress( newDefer.notify );
+ } else {
+ newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );
+ }
+ });
+ });
+ fns = null;
+ }).promise();
+ },
+ // Get a promise for this deferred
+ // If obj is provided, the promise aspect is added to the object
+ promise: function( obj ) {
+ return obj != null ? jQuery.extend( obj, promise ) : promise;
+ }
+ },
+ deferred = {};
- // Keep pipe for back-compat
- promise.pipe = promise.then;
+ // Keep pipe for back-compat
+ promise.pipe = promise.then;
- // Add list-specific methods
- jQuery.each( tuples, function( i, tuple ) {
- var list = tuple[ 2 ],
- stateString = tuple[ 3 ];
+ // Add list-specific methods
+ jQuery.each( tuples, function( i, tuple ) {
+ var list = tuple[ 2 ],
+ stateString = tuple[ 3 ];
- // promise[ done | fail | progress ] = list.add
- promise[ tuple[1] ] = list.add;
+ // promise[ done | fail | progress ] = list.add
+ promise[ tuple[1] ] = list.add;
- // Handle state
- if ( stateString ) {
- list.add(function() {
- // state = [ resolved | rejected ]
- state = stateString;
+ // Handle state
+ if ( stateString ) {
+ list.add(function() {
+ // state = [ resolved | rejected ]
+ state = stateString;
- // [ reject_list | resolve_list ].disable; progress_list.lock
- }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
- }
+ // [ reject_list | resolve_list ].disable; progress_list.lock
+ }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
+ }
- // deferred[ resolve | reject | notify ]
- deferred[ tuple[0] ] = function() {
- deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );
- return this;
- };
- deferred[ tuple[0] + "With" ] = list.fireWith;
- });
+ // deferred[ resolve | reject | notify ]
+ deferred[ tuple[0] ] = function() {
+ deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );
+ return this;
+ };
+ deferred[ tuple[0] + "With" ] = list.fireWith;
+ });
- // Make the deferred a promise
- promise.promise( deferred );
+ // Make the deferred a promise
+ promise.promise( deferred );
- // Call given func if any
- if ( func ) {
- func.call( deferred, deferred );
- }
+ // Call given func if any
+ if ( func ) {
+ func.call( deferred, deferred );
+ }
- // All done!
- return deferred;
- },
+ // All done!
+ return deferred;
+ },
- // Deferred helper
- when: function( subordinate /* , ..., subordinateN */ ) {
- var i = 0,
- resolveValues = core_slice.call( arguments ),
- length = resolveValues.length,
+ // Deferred helper
+ when: function( subordinate /* , ..., subordinateN */ ) {
+ var i = 0,
+ resolveValues = core_slice.call( arguments ),
+ length = resolveValues.length,
- // the count of uncompleted subordinates
- remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,
+ // the count of uncompleted subordinates
+ remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,
- // the master Deferred. If resolveValues consist of only a single Deferred, just use that.
- deferred = remaining === 1 ? subordinate : jQuery.Deferred(),
+ // the master Deferred. If resolveValues consist of only a single Deferred, just use that.
+ deferred = remaining === 1 ? subordinate : jQuery.Deferred(),
- // Update function for both resolve and progress values
- updateFunc = function( i, contexts, values ) {
- return function( value ) {
- contexts[ i ] = this;
- values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value;
- if( values === progressValues ) {
- deferred.notifyWith( contexts, values );
- } else if ( !( --remaining ) ) {
- deferred.resolveWith( contexts, values );
- }
- };
- },
+ // Update function for both resolve and progress values
+ updateFunc = function( i, contexts, values ) {
+ return function( value ) {
+ contexts[ i ] = this;
+ values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value;
+ if( values === progressValues ) {
+ deferred.notifyWith( contexts, values );
+ } else if ( !( --remaining ) ) {
+ deferred.resolveWith( contexts, values );
+ }
+ };
+ },
- progressValues, progressContexts, resolveContexts;
+ progressValues, progressContexts, resolveContexts;
- // add listeners to Deferred subordinates; treat others as resolved
- if ( length > 1 ) {
- progressValues = new Array( length );
- progressContexts = new Array( length );
- resolveContexts = new Array( length );
- for ( ; i < length; i++ ) {
- if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) {
- resolveValues[ i ].promise()
- .done( updateFunc( i, resolveContexts, resolveValues ) )
- .fail( deferred.reject )
- .progress( updateFunc( i, progressContexts, progressValues ) );
- } else {
- --remaining;
- }
- }
- }
+ // add listeners to Deferred subordinates; treat others as resolved
+ if ( length > 1 ) {
+ progressValues = new Array( length );
+ progressContexts = new Array( length );
+ resolveContexts = new Array( length );
+ for ( ; i < length; i++ ) {
+ if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) {
+ resolveValues[ i ].promise()
+ .done( updateFunc( i, resolveContexts, resolveValues ) )
+ .fail( deferred.reject )
+ .progress( updateFunc( i, progressContexts, progressValues ) );
+ } else {
+ --remaining;
+ }
+ }
+ }
- // if we're not waiting on anything, resolve the master
- if ( !remaining ) {
- deferred.resolveWith( resolveContexts, resolveValues );
- }
+ // if we're not waiting on anything, resolve the master
+ if ( !remaining ) {
+ deferred.resolveWith( resolveContexts, resolveValues );
+ }
- return deferred.promise();
- }
+ return deferred.promise();
+ }
});
jQuery.support = (function( support ) {
- var input = document.createElement("input"),
- fragment = document.createDocumentFragment(),
- div = document.createElement("div"),
- select = document.createElement("select"),
- opt = select.appendChild( document.createElement("option") );
+ var input = document.createElement("input"),
+ fragment = document.createDocumentFragment(),
+ div = document.createElement("div"),
+ select = document.createElement("select"),
+ opt = select.appendChild( document.createElement("option") );
- // Finish early in limited environments
- if ( !input.type ) {
- return support;
- }
+ // Finish early in limited environments
+ if ( !input.type ) {
+ return support;
+ }
- input.type = "checkbox";
+ input.type = "checkbox";
- // Support: Safari 5.1, iOS 5.1, Android 4.x, Android 2.3
- // Check the default checkbox/radio value ("" on old WebKit; "on" elsewhere)
- support.checkOn = input.value !== "";
+ // Support: Safari 5.1, iOS 5.1, Android 4.x, Android 2.3
+ // Check the default checkbox/radio value ("" on old WebKit; "on" elsewhere)
+ support.checkOn = input.value !== "";
- // Must access the parent to make an option select properly
- // Support: IE9, IE10
- support.optSelected = opt.selected;
+ // Must access the parent to make an option select properly
+ // Support: IE9, IE10
+ support.optSelected = opt.selected;
- // Will be defined later
- support.reliableMarginRight = true;
- support.boxSizingReliable = true;
- support.pixelPosition = false;
+ // Will be defined later
+ support.reliableMarginRight = true;
+ support.boxSizingReliable = true;
+ support.pixelPosition = false;
- // Make sure checked status is properly cloned
- // Support: IE9, IE10
- input.checked = true;
- support.noCloneChecked = input.cloneNode( true ).checked;
+ // Make sure checked status is properly cloned
+ // Support: IE9, IE10
+ input.checked = true;
+ support.noCloneChecked = input.cloneNode( true ).checked;
- // Make sure that the options inside disabled selects aren't marked as disabled
- // (WebKit marks them as disabled)
- select.disabled = true;
- support.optDisabled = !opt.disabled;
+ // Make sure that the options inside disabled selects aren't marked as disabled
+ // (WebKit marks them as disabled)
+ select.disabled = true;
+ support.optDisabled = !opt.disabled;
- // Check if an input maintains its value after becoming a radio
- // Support: IE9, IE10
- input = document.createElement("input");
- input.value = "t";
- input.type = "radio";
- support.radioValue = input.value === "t";
+ // Check if an input maintains its value after becoming a radio
+ // Support: IE9, IE10
+ input = document.createElement("input");
+ input.value = "t";
+ input.type = "radio";
+ support.radioValue = input.value === "t";
- // #11217 - WebKit loses check when the name is after the checked attribute
- input.setAttribute( "checked", "t" );
- input.setAttribute( "name", "t" );
+ // #11217 - WebKit loses check when the name is after the checked attribute
+ input.setAttribute( "checked", "t" );
+ input.setAttribute( "name", "t" );
- fragment.appendChild( input );
+ fragment.appendChild( input );
- // Support: Safari 5.1, Android 4.x, Android 2.3
- // old WebKit doesn't clone checked state correctly in fragments
- support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked;
+ // Support: Safari 5.1, Android 4.x, Android 2.3
+ // old WebKit doesn't clone checked state correctly in fragments
+ support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked;
- // Support: Firefox, Chrome, Safari
- // Beware of CSP restrictions (https://developer.mozilla.org/en/Security/CSP)
- support.focusinBubbles = "onfocusin" in window;
+ // Support: Firefox, Chrome, Safari
+ // Beware of CSP restrictions (https://developer.mozilla.org/en/Security/CSP)
+ support.focusinBubbles = "onfocusin" in window;
- div.style.backgroundClip = "content-box";
- div.cloneNode( true ).style.backgroundClip = "";
- support.clearCloneStyle = div.style.backgroundClip === "content-box";
+ div.style.backgroundClip = "content-box";
+ div.cloneNode( true ).style.backgroundClip = "";
+ support.clearCloneStyle = div.style.backgroundClip === "content-box";
- // Run tests that need a body at doc ready
- jQuery(function() {
- var container, marginDiv,
- // Support: Firefox, Android 2.3 (Prefixed box-sizing versions).
- divReset = "padding:0;margin:0;border:0;display:block;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box",
- body = document.getElementsByTagName("body")[ 0 ];
+ // Run tests that need a body at doc ready
+ jQuery(function() {
+ var container, marginDiv,
+ // Support: Firefox, Android 2.3 (Prefixed box-sizing versions).
+ divReset = "padding:0;margin:0;border:0;display:block;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box",
+ body = document.getElementsByTagName("body")[ 0 ];
- if ( !body ) {
- // Return for frameset docs that don't have a body
- return;
- }
+ if ( !body ) {
+ // Return for frameset docs that don't have a body
+ return;
+ }
- container = document.createElement("div");
- container.style.cssText = "border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px";
+ container = document.createElement("div");
+ container.style.cssText = "border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px";
- // Check box-sizing and margin behavior.
- body.appendChild( container ).appendChild( div );
- div.innerHTML = "";
- // Support: Firefox, Android 2.3 (Prefixed box-sizing versions).
- div.style.cssText = "-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%";
+ // Check box-sizing and margin behavior.
+ body.appendChild( container ).appendChild( div );
+ div.innerHTML = "";
+ // Support: Firefox, Android 2.3 (Prefixed box-sizing versions).
+ div.style.cssText = "-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%";
- // Workaround failing boxSizing test due to offsetWidth returning wrong value
- // with some non-1 values of body zoom, ticket #13543
- jQuery.swap( body, body.style.zoom != null ? { zoom: 1 } : {}, function() {
- support.boxSizing = div.offsetWidth === 4;
- });
+ // Workaround failing boxSizing test due to offsetWidth returning wrong value
+ // with some non-1 values of body zoom, ticket #13543
+ jQuery.swap( body, body.style.zoom != null ? { zoom: 1 } : {}, function() {
+ support.boxSizing = div.offsetWidth === 4;
+ });
- // Use window.getComputedStyle because jsdom on node.js will break without it.
- if ( window.getComputedStyle ) {
- support.pixelPosition = ( window.getComputedStyle( div, null ) || {} ).top !== "1%";
- support.boxSizingReliable = ( window.getComputedStyle( div, null ) || { width: "4px" } ).width === "4px";
+ // Use window.getComputedStyle because jsdom on node.js will break without it.
+ if ( window.getComputedStyle ) {
+ support.pixelPosition = ( window.getComputedStyle( div, null ) || {} ).top !== "1%";
+ support.boxSizingReliable = ( window.getComputedStyle( div, null ) || { width: "4px" } ).width === "4px";
- // Support: Android 2.3
- // Check if div with explicit width and no margin-right incorrectly
- // gets computed margin-right based on width of container. (#3333)
- // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
- marginDiv = div.appendChild( document.createElement("div") );
- marginDiv.style.cssText = div.style.cssText = divReset;
- marginDiv.style.marginRight = marginDiv.style.width = "0";
- div.style.width = "1px";
+ // Support: Android 2.3
+ // Check if div with explicit width and no margin-right incorrectly
+ // gets computed margin-right based on width of container. (#3333)
+ // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
+ marginDiv = div.appendChild( document.createElement("div") );
+ marginDiv.style.cssText = div.style.cssText = divReset;
+ marginDiv.style.marginRight = marginDiv.style.width = "0";
+ div.style.width = "1px";
- support.reliableMarginRight =
- !parseFloat( ( window.getComputedStyle( marginDiv, null ) || {} ).marginRight );
- }
+ support.reliableMarginRight =
+ !parseFloat( ( window.getComputedStyle( marginDiv, null ) || {} ).marginRight );
+ }
- body.removeChild( container );
- });
+ body.removeChild( container );
+ });
- return support;
+ return support;
})( {} );
/*
- Implementation Summary
+ Implementation Summary
- 1. Enforce API surface and semantic compatibility with 1.9.x branch
- 2. Improve the module's maintainability by reducing the storage
- paths to a single mechanism.
- 3. Use the same single mechanism to support "private" and "user" data.
- 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData)
- 5. Avoid exposing implementation details on user objects (eg. expando properties)
- 6. Provide a clear path for implementation upgrade to WeakMap in 2014
+ 1. Enforce API surface and semantic compatibility with 1.9.x branch
+ 2. Improve the module's maintainability by reducing the storage
+ paths to a single mechanism.
+ 3. Use the same single mechanism to support "private" and "user" data.
+ 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData)
+ 5. Avoid exposing implementation details on user objects (eg. expando properties)
+ 6. Provide a clear path for implementation upgrade to WeakMap in 2014
*/
var data_user, data_priv,
- rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/,
- rmultiDash = /([A-Z])/g;
+ rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/,
+ rmultiDash = /([A-Z])/g;
function Data() {
- // Support: Android < 4,
- // Old WebKit does not have Object.preventExtensions/freeze method,
- // return new empty object instead with no [[set]] accessor
- Object.defineProperty( this.cache = {}, 0, {
- get: function() {
- return {};
- }
- });
+ // Support: Android < 4,
+ // Old WebKit does not have Object.preventExtensions/freeze method,
+ // return new empty object instead with no [[set]] accessor
+ Object.defineProperty( this.cache = {}, 0, {
+ get: function() {
+ return {};
+ }
+ });
- this.expando = jQuery.expando + Math.random();
+ this.expando = jQuery.expando + Math.random();
}
Data.uid = 1;
Data.accepts = function( owner ) {
- // Accepts only:
- // - Node
- // - Node.ELEMENT_NODE
- // - Node.DOCUMENT_NODE
- // - Object
- // - Any
- return owner.nodeType ?
- owner.nodeType === 1 || owner.nodeType === 9 : true;
+ // Accepts only:
+ // - Node
+ // - Node.ELEMENT_NODE
+ // - Node.DOCUMENT_NODE
+ // - Object
+ // - Any
+ return owner.nodeType ?
+ owner.nodeType === 1 || owner.nodeType === 9 : true;
};
Data.prototype = {
- key: function( owner ) {
- // We can accept data for non-element nodes in modern browsers,
- // but we should not, see #8335.
- // Always return the key for a frozen object.
- if ( !Data.accepts( owner ) ) {
- return 0;
- }
+ key: function( owner ) {
+ // We can accept data for non-element nodes in modern browsers,
+ // but we should not, see #8335.
+ // Always return the key for a frozen object.
+ if ( !Data.accepts( owner ) ) {
+ return 0;
+ }
- var descriptor = {},
- // Check if the owner object already has a cache key
- unlock = owner[ this.expando ];
+ var descriptor = {},
+ // Check if the owner object already has a cache key
+ unlock = owner[ this.expando ];
- // If not, create one
- if ( !unlock ) {
- unlock = Data.uid++;
+ // If not, create one
+ if ( !unlock ) {
+ unlock = Data.uid++;
- // Secure it in a non-enumerable, non-writable property
- try {
- descriptor[ this.expando ] = { value: unlock };
- Object.defineProperties( owner, descriptor );
+ // Secure it in a non-enumerable, non-writable property
+ try {
+ descriptor[ this.expando ] = { value: unlock };
+ Object.defineProperties( owner, descriptor );
- // Support: Android < 4
- // Fallback to a less secure definition
- } catch ( e ) {
- descriptor[ this.expando ] = unlock;
- jQuery.extend( owner, descriptor );
- }
- }
+ // Support: Android < 4
+ // Fallback to a less secure definition
+ } catch ( e ) {
+ descriptor[ this.expando ] = unlock;
+ jQuery.extend( owner, descriptor );
+ }
+ }
- // Ensure the cache object
- if ( !this.cache[ unlock ] ) {
- this.cache[ unlock ] = {};
- }
+ // Ensure the cache object
+ if ( !this.cache[ unlock ] ) {
+ this.cache[ unlock ] = {};
+ }
- return unlock;
- },
- set: function( owner, data, value ) {
- var prop,
- // There may be an unlock assigned to this node,
- // if there is no entry for this "owner", create one inline
- // and set the unlock as though an owner entry had always existed
- unlock = this.key( owner ),
- cache = this.cache[ unlock ];
+ return unlock;
+ },
+ set: function( owner, data, value ) {
+ var prop,
+ // There may be an unlock assigned to this node,
+ // if there is no entry for this "owner", create one inline
+ // and set the unlock as though an owner entry had always existed
+ unlock = this.key( owner ),
+ cache = this.cache[ unlock ];
- // Handle: [ owner, key, value ] args
- if ( typeof data === "string" ) {
- cache[ data ] = value;
+ // Handle: [ owner, key, value ] args
+ if ( typeof data === "string" ) {
+ cache[ data ] = value;
- // Handle: [ owner, { properties } ] args
- } else {
- // Fresh assignments by object are shallow copied
- if ( jQuery.isEmptyObject( cache ) ) {
- jQuery.extend( this.cache[ unlock ], data );
- // Otherwise, copy the properties one-by-one to the cache object
- } else {
- for ( prop in data ) {
- cache[ prop ] = data[ prop ];
- }
- }
- }
- return cache;
- },
- get: function( owner, key ) {
- // Either a valid cache is found, or will be created.
- // New caches will be created and the unlock returned,
- // allowing direct access to the newly created
- // empty data object. A valid owner object must be provided.
- var cache = this.cache[ this.key( owner ) ];
+ // Handle: [ owner, { properties } ] args
+ } else {
+ // Fresh assignments by object are shallow copied
+ if ( jQuery.isEmptyObject( cache ) ) {
+ jQuery.extend( this.cache[ unlock ], data );
+ // Otherwise, copy the properties one-by-one to the cache object
+ } else {
+ for ( prop in data ) {
+ cache[ prop ] = data[ prop ];
+ }
+ }
+ }
+ return cache;
+ },
+ get: function( owner, key ) {
+ // Either a valid cache is found, or will be created.
+ // New caches will be created and the unlock returned,
+ // allowing direct access to the newly created
+ // empty data object. A valid owner object must be provided.
+ var cache = this.cache[ this.key( owner ) ];
- return key === undefined ?
- cache : cache[ key ];
- },
- access: function( owner, key, value ) {
- // In cases where either:
- //
- // 1. No key was specified
- // 2. A string key was specified, but no value provided
- //
- // Take the "read" path and allow the get method to determine
- // which value to return, respectively either:
- //
- // 1. The entire cache object
- // 2. The data stored at the key
- //
- if ( key === undefined ||
- ((key && typeof key === "string") && value === undefined) ) {
- return this.get( owner, key );
- }
+ return key === undefined ?
+ cache : cache[ key ];
+ },
+ access: function( owner, key, value ) {
+ // In cases where either:
+ //
+ // 1. No key was specified
+ // 2. A string key was specified, but no value provided
+ //
+ // Take the "read" path and allow the get method to determine
+ // which value to return, respectively either:
+ //
+ // 1. The entire cache object
+ // 2. The data stored at the key
+ //
+ if ( key === undefined ||
+ ((key && typeof key === "string") && value === undefined) ) {
+ return this.get( owner, key );
+ }
- // [*]When the key is not a string, or both a key and value
- // are specified, set or extend (existing objects) with either:
- //
- // 1. An object of properties
- // 2. A key and value
- //
- this.set( owner, key, value );
+ // [*]When the key is not a string, or both a key and value
+ // are specified, set or extend (existing objects) with either:
+ //
+ // 1. An object of properties
+ // 2. A key and value
+ //
+ this.set( owner, key, value );
- // Since the "set" path can have two possible entry points
- // return the expected data based on which path was taken[*]
- return value !== undefined ? value : key;
- },
- remove: function( owner, key ) {
- var i, name, camel,
- unlock = this.key( owner ),
- cache = this.cache[ unlock ];
+ // Since the "set" path can have two possible entry points
+ // return the expected data based on which path was taken[*]
+ return value !== undefined ? value : key;
+ },
+ remove: function( owner, key ) {
+ var i, name, camel,
+ unlock = this.key( owner ),
+ cache = this.cache[ unlock ];
- if ( key === undefined ) {
- this.cache[ unlock ] = {};
+ if ( key === undefined ) {
+ this.cache[ unlock ] = {};
- } else {
- // Support array or space separated string of keys
- if ( jQuery.isArray( key ) ) {
- // If "name" is an array of keys...
- // When data is initially created, via ("key", "val") signature,
- // keys will be converted to camelCase.
- // Since there is no way to tell _how_ a key was added, remove
- // both plain key and camelCase key. #12786
- // This will only penalize the array argument path.
- name = key.concat( key.map( jQuery.camelCase ) );
- } else {
- camel = jQuery.camelCase( key );
- // Try the string as a key before any manipulation
- if ( key in cache ) {
- name = [ key, camel ];
- } else {
- // If a key with the spaces exists, use it.
- // Otherwise, create an array by matching non-whitespace
- name = camel;
- name = name in cache ?
- [ name ] : ( name.match( core_rnotwhite ) || [] );
- }
- }
+ } else {
+ // Support array or space separated string of keys
+ if ( jQuery.isArray( key ) ) {
+ // If "name" is an array of keys...
+ // When data is initially created, via ("key", "val") signature,
+ // keys will be converted to camelCase.
+ // Since there is no way to tell _how_ a key was added, remove
+ // both plain key and camelCase key. #12786
+ // This will only penalize the array argument path.
+ name = key.concat( key.map( jQuery.camelCase ) );
+ } else {
+ camel = jQuery.camelCase( key );
+ // Try the string as a key before any manipulation
+ if ( key in cache ) {
+ name = [ key, camel ];
+ } else {
+ // If a key with the spaces exists, use it.
+ // Otherwise, create an array by matching non-whitespace
+ name = camel;
+ name = name in cache ?
+ [ name ] : ( name.match( core_rnotwhite ) || [] );
+ }
+ }
- i = name.length;
- while ( i-- ) {
- delete cache[ name[ i ] ];
- }
- }
- },
- hasData: function( owner ) {
- return !jQuery.isEmptyObject(
- this.cache[ owner[ this.expando ] ] || {}
- );
- },
- discard: function( owner ) {
- if ( owner[ this.expando ] ) {
- delete this.cache[ owner[ this.expando ] ];
- }
- }
+ i = name.length;
+ while ( i-- ) {
+ delete cache[ name[ i ] ];
+ }
+ }
+ },
+ hasData: function( owner ) {
+ return !jQuery.isEmptyObject(
+ this.cache[ owner[ this.expando ] ] || {}
+ );
+ },
+ discard: function( owner ) {
+ if ( owner[ this.expando ] ) {
+ delete this.cache[ owner[ this.expando ] ];
+ }
+ }
};
// These may be used throughout the jQuery core codebase
@@ -3519,817 +3519,817 @@ data_priv = new Data();
jQuery.extend({
- acceptData: Data.accepts,
+ acceptData: Data.accepts,
- hasData: function( elem ) {
- return data_user.hasData( elem ) || data_priv.hasData( elem );
- },
+ hasData: function( elem ) {
+ return data_user.hasData( elem ) || data_priv.hasData( elem );
+ },
- data: function( elem, name, data ) {
- return data_user.access( elem, name, data );
- },
+ data: function( elem, name, data ) {
+ return data_user.access( elem, name, data );
+ },
- removeData: function( elem, name ) {
- data_user.remove( elem, name );
- },
+ removeData: function( elem, name ) {
+ data_user.remove( elem, name );
+ },
- // TODO: Now that all calls to _data and _removeData have been replaced
- // with direct calls to data_priv methods, these can be deprecated.
- _data: function( elem, name, data ) {
- return data_priv.access( elem, name, data );
- },
+ // TODO: Now that all calls to _data and _removeData have been replaced
+ // with direct calls to data_priv methods, these can be deprecated.
+ _data: function( elem, name, data ) {
+ return data_priv.access( elem, name, data );
+ },
- _removeData: function( elem, name ) {
- data_priv.remove( elem, name );
- }
+ _removeData: function( elem, name ) {
+ data_priv.remove( elem, name );
+ }
});
jQuery.fn.extend({
- data: function( key, value ) {
- var attrs, name,
- elem = this[ 0 ],
- i = 0,
- data = null;
+ data: function( key, value ) {
+ var attrs, name,
+ elem = this[ 0 ],
+ i = 0,
+ data = null;
- // Gets all values
- if ( key === undefined ) {
- if ( this.length ) {
- data = data_user.get( elem );
+ // Gets all values
+ if ( key === undefined ) {
+ if ( this.length ) {
+ data = data_user.get( elem );
- if ( elem.nodeType === 1 && !data_priv.get( elem, "hasDataAttrs" ) ) {
- attrs = elem.attributes;
- for ( ; i < attrs.length; i++ ) {
- name = attrs[ i ].name;
+ if ( elem.nodeType === 1 && !data_priv.get( elem, "hasDataAttrs" ) ) {
+ attrs = elem.attributes;
+ for ( ; i < attrs.length; i++ ) {
+ name = attrs[ i ].name;
- if ( name.indexOf( "data-" ) === 0 ) {
- name = jQuery.camelCase( name.slice(5) );
- dataAttr( elem, name, data[ name ] );
- }
- }
- data_priv.set( elem, "hasDataAttrs", true );
- }
- }
+ if ( name.indexOf( "data-" ) === 0 ) {
+ name = jQuery.camelCase( name.slice(5) );
+ dataAttr( elem, name, data[ name ] );
+ }
+ }
+ data_priv.set( elem, "hasDataAttrs", true );
+ }
+ }
- return data;
- }
+ return data;
+ }
- // Sets multiple values
- if ( typeof key === "object" ) {
- return this.each(function() {
- data_user.set( this, key );
- });
- }
+ // Sets multiple values
+ if ( typeof key === "object" ) {
+ return this.each(function() {
+ data_user.set( this, key );
+ });
+ }
- return jQuery.access( this, function( value ) {
- var data,
- camelKey = jQuery.camelCase( key );
+ return jQuery.access( this, function( value ) {
+ var data,
+ camelKey = jQuery.camelCase( key );
- // The calling jQuery object (element matches) is not empty
- // (and therefore has an element appears at this[ 0 ]) and the
- // `value` parameter was not undefined. An empty jQuery object
- // will result in `undefined` for elem = this[ 0 ] which will
- // throw an exception if an attempt to read a data cache is made.
- if ( elem && value === undefined ) {
- // Attempt to get data from the cache
- // with the key as-is
- data = data_user.get( elem, key );
- if ( data !== undefined ) {
- return data;
- }
+ // The calling jQuery object (element matches) is not empty
+ // (and therefore has an element appears at this[ 0 ]) and the
+ // `value` parameter was not undefined. An empty jQuery object
+ // will result in `undefined` for elem = this[ 0 ] which will
+ // throw an exception if an attempt to read a data cache is made.
+ if ( elem && value === undefined ) {
+ // Attempt to get data from the cache
+ // with the key as-is
+ data = data_user.get( elem, key );
+ if ( data !== undefined ) {
+ return data;
+ }
- // Attempt to get data from the cache
- // with the key camelized
- data = data_user.get( elem, camelKey );
- if ( data !== undefined ) {
- return data;
- }
+ // Attempt to get data from the cache
+ // with the key camelized
+ data = data_user.get( elem, camelKey );
+ if ( data !== undefined ) {
+ return data;
+ }
- // Attempt to "discover" the data in
- // HTML5 custom data-* attrs
- data = dataAttr( elem, camelKey, undefined );
- if ( data !== undefined ) {
- return data;
- }
+ // Attempt to "discover" the data in
+ // HTML5 custom data-* attrs
+ data = dataAttr( elem, camelKey, undefined );
+ if ( data !== undefined ) {
+ return data;
+ }
- // We tried really hard, but the data doesn't exist.
- return;
- }
+ // We tried really hard, but the data doesn't exist.
+ return;
+ }
- // Set the data...
- this.each(function() {
- // First, attempt to store a copy or reference of any
- // data that might've been store with a camelCased key.
- var data = data_user.get( this, camelKey );
+ // Set the data...
+ this.each(function() {
+ // First, attempt to store a copy or reference of any
+ // data that might've been store with a camelCased key.
+ var data = data_user.get( this, camelKey );
- // For HTML5 data-* attribute interop, we have to
- // store property names with dashes in a camelCase form.
- // This might not apply to all properties...*
- data_user.set( this, camelKey, value );
+ // For HTML5 data-* attribute interop, we have to
+ // store property names with dashes in a camelCase form.
+ // This might not apply to all properties...*
+ data_user.set( this, camelKey, value );
- // *... In the case of properties that might _actually_
- // have dashes, we need to also store a copy of that
- // unchanged property.
- if ( key.indexOf("-") !== -1 && data !== undefined ) {
- data_user.set( this, key, value );
- }
- });
- }, null, value, arguments.length > 1, null, true );
- },
+ // *... In the case of properties that might _actually_
+ // have dashes, we need to also store a copy of that
+ // unchanged property.
+ if ( key.indexOf("-") !== -1 && data !== undefined ) {
+ data_user.set( this, key, value );
+ }
+ });
+ }, null, value, arguments.length > 1, null, true );
+ },
- removeData: function( key ) {
- return this.each(function() {
- data_user.remove( this, key );
- });
- }
+ removeData: function( key ) {
+ return this.each(function() {
+ data_user.remove( this, key );
+ });
+ }
});
function dataAttr( elem, key, data ) {
- var name;
+ var name;
- // If nothing was found internally, try to fetch any
- // data from the HTML5 data-* attribute
- if ( data === undefined && elem.nodeType === 1 ) {
- name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase();
- data = elem.getAttribute( name );
+ // If nothing was found internally, try to fetch any
+ // data from the HTML5 data-* attribute
+ if ( data === undefined && elem.nodeType === 1 ) {
+ name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase();
+ data = elem.getAttribute( name );
- if ( typeof data === "string" ) {
- try {
- data = data === "true" ? true :
- data === "false" ? false :
- data === "null" ? null :
- // Only convert to a number if it doesn't change the string
- +data + "" === data ? +data :
- rbrace.test( data ) ? JSON.parse( data ) :
- data;
- } catch( e ) {}
+ if ( typeof data === "string" ) {
+ try {
+ data = data === "true" ? true :
+ data === "false" ? false :
+ data === "null" ? null :
+ // Only convert to a number if it doesn't change the string
+ +data + "" === data ? +data :
+ rbrace.test( data ) ? JSON.parse( data ) :
+ data;
+ } catch( e ) {}
- // Make sure we set the data so it isn't changed later
- data_user.set( elem, key, data );
- } else {
- data = undefined;
- }
- }
- return data;
+ // Make sure we set the data so it isn't changed later
+ data_user.set( elem, key, data );
+ } else {
+ data = undefined;
+ }
+ }
+ return data;
}
jQuery.extend({
- queue: function( elem, type, data ) {
- var queue;
+ queue: function( elem, type, data ) {
+ var queue;
- if ( elem ) {
- type = ( type || "fx" ) + "queue";
- queue = data_priv.get( elem, type );
+ if ( elem ) {
+ type = ( type || "fx" ) + "queue";
+ queue = data_priv.get( elem, type );
- // Speed up dequeue by getting out quickly if this is just a lookup
- if ( data ) {
- if ( !queue || jQuery.isArray( data ) ) {
- queue = data_priv.access( elem, type, jQuery.makeArray(data) );
- } else {
- queue.push( data );
- }
- }
- return queue || [];
- }
- },
+ // Speed up dequeue by getting out quickly if this is just a lookup
+ if ( data ) {
+ if ( !queue || jQuery.isArray( data ) ) {
+ queue = data_priv.access( elem, type, jQuery.makeArray(data) );
+ } else {
+ queue.push( data );
+ }
+ }
+ return queue || [];
+ }
+ },
- dequeue: function( elem, type ) {
- type = type || "fx";
+ dequeue: function( elem, type ) {
+ type = type || "fx";
- var queue = jQuery.queue( elem, type ),
- startLength = queue.length,
- fn = queue.shift(),
- hooks = jQuery._queueHooks( elem, type ),
- next = function() {
- jQuery.dequeue( elem, type );
- };
+ var queue = jQuery.queue( elem, type ),
+ startLength = queue.length,
+ fn = queue.shift(),
+ hooks = jQuery._queueHooks( elem, type ),
+ next = function() {
+ jQuery.dequeue( elem, type );
+ };
- // If the fx queue is dequeued, always remove the progress sentinel
- if ( fn === "inprogress" ) {
- fn = queue.shift();
- startLength--;
- }
+ // If the fx queue is dequeued, always remove the progress sentinel
+ if ( fn === "inprogress" ) {
+ fn = queue.shift();
+ startLength--;
+ }
- if ( fn ) {
+ if ( fn ) {
- // Add a progress sentinel to prevent the fx queue from being
- // automatically dequeued
- if ( type === "fx" ) {
- queue.unshift( "inprogress" );
- }
+ // Add a progress sentinel to prevent the fx queue from being
+ // automatically dequeued
+ if ( type === "fx" ) {
+ queue.unshift( "inprogress" );
+ }
- // clear up the last queue stop function
- delete hooks.stop;
- fn.call( elem, next, hooks );
- }
+ // clear up the last queue stop function
+ delete hooks.stop;
+ fn.call( elem, next, hooks );
+ }
- if ( !startLength && hooks ) {
- hooks.empty.fire();
- }
- },
+ if ( !startLength && hooks ) {
+ hooks.empty.fire();
+ }
+ },
- // not intended for public consumption - generates a queueHooks object, or returns the current one
- _queueHooks: function( elem, type ) {
- var key = type + "queueHooks";
- return data_priv.get( elem, key ) || data_priv.access( elem, key, {
- empty: jQuery.Callbacks("once memory").add(function() {
- data_priv.remove( elem, [ type + "queue", key ] );
- })
- });
- }
+ // not intended for public consumption - generates a queueHooks object, or returns the current one
+ _queueHooks: function( elem, type ) {
+ var key = type + "queueHooks";
+ return data_priv.get( elem, key ) || data_priv.access( elem, key, {
+ empty: jQuery.Callbacks("once memory").add(function() {
+ data_priv.remove( elem, [ type + "queue", key ] );
+ })
+ });
+ }
});
jQuery.fn.extend({
- queue: function( type, data ) {
- var setter = 2;
+ queue: function( type, data ) {
+ var setter = 2;
- if ( typeof type !== "string" ) {
- data = type;
- type = "fx";
- setter--;
- }
+ if ( typeof type !== "string" ) {
+ data = type;
+ type = "fx";
+ setter--;
+ }
- if ( arguments.length < setter ) {
- return jQuery.queue( this[0], type );
- }
+ if ( arguments.length < setter ) {
+ return jQuery.queue( this[0], type );
+ }
- return data === undefined ?
- this :
- this.each(function() {
- var queue = jQuery.queue( this, type, data );
+ return data === undefined ?
+ this :
+ this.each(function() {
+ var queue = jQuery.queue( this, type, data );
- // ensure a hooks for this queue
- jQuery._queueHooks( this, type );
+ // ensure a hooks for this queue
+ jQuery._queueHooks( this, type );
- if ( type === "fx" && queue[0] !== "inprogress" ) {
- jQuery.dequeue( this, type );
- }
- });
- },
- dequeue: function( type ) {
- return this.each(function() {
- jQuery.dequeue( this, type );
- });
- },
- // Based off of the plugin by Clint Helfers, with permission.
- // http://blindsignals.com/index.php/2009/07/jquery-delay/
- delay: function( time, type ) {
- time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;
- type = type || "fx";
+ if ( type === "fx" && queue[0] !== "inprogress" ) {
+ jQuery.dequeue( this, type );
+ }
+ });
+ },
+ dequeue: function( type ) {
+ return this.each(function() {
+ jQuery.dequeue( this, type );
+ });
+ },
+ // Based off of the plugin by Clint Helfers, with permission.
+ // http://blindsignals.com/index.php/2009/07/jquery-delay/
+ delay: function( time, type ) {
+ time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;
+ type = type || "fx";
- return this.queue( type, function( next, hooks ) {
- var timeout = setTimeout( next, time );
- hooks.stop = function() {
- clearTimeout( timeout );
- };
- });
- },
- clearQueue: function( type ) {
- return this.queue( type || "fx", [] );
- },
- // Get a promise resolved when queues of a certain type
- // are emptied (fx is the type by default)
- promise: function( type, obj ) {
- var tmp,
- count = 1,
- defer = jQuery.Deferred(),
- elements = this,
- i = this.length,
- resolve = function() {
- if ( !( --count ) ) {
- defer.resolveWith( elements, [ elements ] );
- }
- };
+ return this.queue( type, function( next, hooks ) {
+ var timeout = setTimeout( next, time );
+ hooks.stop = function() {
+ clearTimeout( timeout );
+ };
+ });
+ },
+ clearQueue: function( type ) {
+ return this.queue( type || "fx", [] );
+ },
+ // Get a promise resolved when queues of a certain type
+ // are emptied (fx is the type by default)
+ promise: function( type, obj ) {
+ var tmp,
+ count = 1,
+ defer = jQuery.Deferred(),
+ elements = this,
+ i = this.length,
+ resolve = function() {
+ if ( !( --count ) ) {
+ defer.resolveWith( elements, [ elements ] );
+ }
+ };
- if ( typeof type !== "string" ) {
- obj = type;
- type = undefined;
- }
- type = type || "fx";
+ if ( typeof type !== "string" ) {
+ obj = type;
+ type = undefined;
+ }
+ type = type || "fx";
- while( i-- ) {
- tmp = data_priv.get( elements[ i ], type + "queueHooks" );
- if ( tmp && tmp.empty ) {
- count++;
- tmp.empty.add( resolve );
- }
- }
- resolve();
- return defer.promise( obj );
- }
+ while( i-- ) {
+ tmp = data_priv.get( elements[ i ], type + "queueHooks" );
+ if ( tmp && tmp.empty ) {
+ count++;
+ tmp.empty.add( resolve );
+ }
+ }
+ resolve();
+ return defer.promise( obj );
+ }
});
var nodeHook, boolHook,
- rclass = /[\t\r\n\f]/g,
- rreturn = /\r/g,
- rfocusable = /^(?:input|select|textarea|button)$/i;
+ rclass = /[\t\r\n\f]/g,
+ rreturn = /\r/g,
+ rfocusable = /^(?:input|select|textarea|button)$/i;
jQuery.fn.extend({
- attr: function( name, value ) {
- return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 );
- },
+ attr: function( name, value ) {
+ return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 );
+ },
- removeAttr: function( name ) {
- return this.each(function() {
- jQuery.removeAttr( this, name );
- });
- },
+ removeAttr: function( name ) {
+ return this.each(function() {
+ jQuery.removeAttr( this, name );
+ });
+ },
- prop: function( name, value ) {
- return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 );
- },
+ prop: function( name, value ) {
+ return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 );
+ },
- removeProp: function( name ) {
- return this.each(function() {
- delete this[ jQuery.propFix[ name ] || name ];
- });
- },
+ removeProp: function( name ) {
+ return this.each(function() {
+ delete this[ jQuery.propFix[ name ] || name ];
+ });
+ },
- addClass: function( value ) {
- var classes, elem, cur, clazz, j,
- i = 0,
- len = this.length,
- proceed = typeof value === "string" && value;
+ addClass: function( value ) {
+ var classes, elem, cur, clazz, j,
+ i = 0,
+ len = this.length,
+ proceed = typeof value === "string" && value;
- if ( jQuery.isFunction( value ) ) {
- return this.each(function( j ) {
- jQuery( this ).addClass( value.call( this, j, this.className ) );
- });
- }
+ if ( jQuery.isFunction( value ) ) {
+ return this.each(function( j ) {
+ jQuery( this ).addClass( value.call( this, j, this.className ) );
+ });
+ }
- if ( proceed ) {
- // The disjunction here is for better compressibility (see removeClass)
- classes = ( value || "" ).match( core_rnotwhite ) || [];
+ if ( proceed ) {
+ // The disjunction here is for better compressibility (see removeClass)
+ classes = ( value || "" ).match( core_rnotwhite ) || [];
- for ( ; i < len; i++ ) {
- elem = this[ i ];
- cur = elem.nodeType === 1 && ( elem.className ?
- ( " " + elem.className + " " ).replace( rclass, " " ) :
- " "
- );
+ for ( ; i < len; i++ ) {
+ elem = this[ i ];
+ cur = elem.nodeType === 1 && ( elem.className ?
+ ( " " + elem.className + " " ).replace( rclass, " " ) :
+ " "
+ );
- if ( cur ) {
- j = 0;
- while ( (clazz = classes[j++]) ) {
- if ( cur.indexOf( " " + clazz + " " ) < 0 ) {
- cur += clazz + " ";
- }
- }
- elem.className = jQuery.trim( cur );
+ if ( cur ) {
+ j = 0;
+ while ( (clazz = classes[j++]) ) {
+ if ( cur.indexOf( " " + clazz + " " ) < 0 ) {
+ cur += clazz + " ";
+ }
+ }
+ elem.className = jQuery.trim( cur );
- }
- }
- }
+ }
+ }
+ }
- return this;
- },
+ return this;
+ },
- removeClass: function( value ) {
- var classes, elem, cur, clazz, j,
- i = 0,
- len = this.length,
- proceed = arguments.length === 0 || typeof value === "string" && value;
+ removeClass: function( value ) {
+ var classes, elem, cur, clazz, j,
+ i = 0,
+ len = this.length,
+ proceed = arguments.length === 0 || typeof value === "string" && value;
- if ( jQuery.isFunction( value ) ) {
- return this.each(function( j ) {
- jQuery( this ).removeClass( value.call( this, j, this.className ) );
- });
- }
- if ( proceed ) {
- classes = ( value || "" ).match( core_rnotwhite ) || [];
+ if ( jQuery.isFunction( value ) ) {
+ return this.each(function( j ) {
+ jQuery( this ).removeClass( value.call( this, j, this.className ) );
+ });
+ }
+ if ( proceed ) {
+ classes = ( value || "" ).match( core_rnotwhite ) || [];
- for ( ; i < len; i++ ) {
- elem = this[ i ];
- // This expression is here for better compressibility (see addClass)
- cur = elem.nodeType === 1 && ( elem.className ?
- ( " " + elem.className + " " ).replace( rclass, " " ) :
- ""
- );
+ for ( ; i < len; i++ ) {
+ elem = this[ i ];
+ // This expression is here for better compressibility (see addClass)
+ cur = elem.nodeType === 1 && ( elem.className ?
+ ( " " + elem.className + " " ).replace( rclass, " " ) :
+ ""
+ );
- if ( cur ) {
- j = 0;
- while ( (clazz = classes[j++]) ) {
- // Remove *all* instances
- while ( cur.indexOf( " " + clazz + " " ) >= 0 ) {
- cur = cur.replace( " " + clazz + " ", " " );
- }
- }
- elem.className = value ? jQuery.trim( cur ) : "";
- }
- }
- }
+ if ( cur ) {
+ j = 0;
+ while ( (clazz = classes[j++]) ) {
+ // Remove *all* instances
+ while ( cur.indexOf( " " + clazz + " " ) >= 0 ) {
+ cur = cur.replace( " " + clazz + " ", " " );
+ }
+ }
+ elem.className = value ? jQuery.trim( cur ) : "";
+ }
+ }
+ }
- return this;
- },
+ return this;
+ },
- toggleClass: function( value, stateVal ) {
- var type = typeof value,
- isBool = typeof stateVal === "boolean";
+ toggleClass: function( value, stateVal ) {
+ var type = typeof value,
+ isBool = typeof stateVal === "boolean";
- if ( jQuery.isFunction( value ) ) {
- return this.each(function( i ) {
- jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal );
- });
- }
+ if ( jQuery.isFunction( value ) ) {
+ return this.each(function( i ) {
+ jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal );
+ });
+ }
- return this.each(function() {
- if ( type === "string" ) {
- // toggle individual class names
- var className,
- i = 0,
- self = jQuery( this ),
- state = stateVal,
- classNames = value.match( core_rnotwhite ) || [];
+ return this.each(function() {
+ if ( type === "string" ) {
+ // toggle individual class names
+ var className,
+ i = 0,
+ self = jQuery( this ),
+ state = stateVal,
+ classNames = value.match( core_rnotwhite ) || [];
- while ( (className = classNames[ i++ ]) ) {
- // check each className given, space separated list
- state = isBool ? state : !self.hasClass( className );
- self[ state ? "addClass" : "removeClass" ]( className );
- }
+ while ( (className = classNames[ i++ ]) ) {
+ // check each className given, space separated list
+ state = isBool ? state : !self.hasClass( className );
+ self[ state ? "addClass" : "removeClass" ]( className );
+ }
- // Toggle whole class name
- } else if ( type === core_strundefined || type === "boolean" ) {
- if ( this.className ) {
- // store className if set
- data_priv.set( this, "__className__", this.className );
- }
+ // Toggle whole class name
+ } else if ( type === core_strundefined || type === "boolean" ) {
+ if ( this.className ) {
+ // store className if set
+ data_priv.set( this, "__className__", this.className );
+ }
- // If the element has a class name or if we're passed "false",
- // then remove the whole classname (if there was one, the above saved it).
- // Otherwise bring back whatever was previously saved (if anything),
- // falling back to the empty string if nothing was stored.
- this.className = this.className || value === false ? "" : data_priv.get( this, "__className__" ) || "";
- }
- });
- },
+ // If the element has a class name or if we're passed "false",
+ // then remove the whole classname (if there was one, the above saved it).
+ // Otherwise bring back whatever was previously saved (if anything),
+ // falling back to the empty string if nothing was stored.
+ this.className = this.className || value === false ? "" : data_priv.get( this, "__className__" ) || "";
+ }
+ });
+ },
- hasClass: function( selector ) {
- var className = " " + selector + " ",
- i = 0,
- l = this.length;
- for ( ; i < l; i++ ) {
- if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) >= 0 ) {
- return true;
- }
- }
+ hasClass: function( selector ) {
+ var className = " " + selector + " ",
+ i = 0,
+ l = this.length;
+ for ( ; i < l; i++ ) {
+ if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) >= 0 ) {
+ return true;
+ }
+ }
- return false;
- },
+ return false;
+ },
- val: function( value ) {
- var hooks, ret, isFunction,
- elem = this[0];
+ val: function( value ) {
+ var hooks, ret, isFunction,
+ elem = this[0];
- if ( !arguments.length ) {
- if ( elem ) {
- hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ];
+ if ( !arguments.length ) {
+ if ( elem ) {
+ hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ];
- if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) {
- return ret;
- }
+ if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) {
+ return ret;
+ }
- ret = elem.value;
+ ret = elem.value;
- return typeof ret === "string" ?
- // handle most common string cases
- ret.replace(rreturn, "") :
- // handle cases where value is null/undef or number
- ret == null ? "" : ret;
- }
+ return typeof ret === "string" ?
+ // handle most common string cases
+ ret.replace(rreturn, "") :
+ // handle cases where value is null/undef or number
+ ret == null ? "" : ret;
+ }
- return;
- }
+ return;
+ }
- isFunction = jQuery.isFunction( value );
+ isFunction = jQuery.isFunction( value );
- return this.each(function( i ) {
- var val;
+ return this.each(function( i ) {
+ var val;
- if ( this.nodeType !== 1 ) {
- return;
- }
+ if ( this.nodeType !== 1 ) {
+ return;
+ }
- if ( isFunction ) {
- val = value.call( this, i, jQuery( this ).val() );
- } else {
- val = value;
- }
+ if ( isFunction ) {
+ val = value.call( this, i, jQuery( this ).val() );
+ } else {
+ val = value;
+ }
- // Treat null/undefined as ""; convert numbers to string
- if ( val == null ) {
- val = "";
- } else if ( typeof val === "number" ) {
- val += "";
- } else if ( jQuery.isArray( val ) ) {
- val = jQuery.map(val, function ( value ) {
- return value == null ? "" : value + "";
- });
- }
+ // Treat null/undefined as ""; convert numbers to string
+ if ( val == null ) {
+ val = "";
+ } else if ( typeof val === "number" ) {
+ val += "";
+ } else if ( jQuery.isArray( val ) ) {
+ val = jQuery.map(val, function ( value ) {
+ return value == null ? "" : value + "";
+ });
+ }
- hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];
+ hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];
- // If set returns undefined, fall back to normal setting
- if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) {
- this.value = val;
- }
- });
- }
+ // If set returns undefined, fall back to normal setting
+ if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) {
+ this.value = val;
+ }
+ });
+ }
});
jQuery.extend({
- valHooks: {
- option: {
- get: function( elem ) {
- // attributes.value is undefined in Blackberry 4.7 but
- // uses .value. See #6932
- var val = elem.attributes.value;
- return !val || val.specified ? elem.value : elem.text;
- }
- },
- select: {
- get: function( elem ) {
- var value, option,
- options = elem.options,
- index = elem.selectedIndex,
- one = elem.type === "select-one" || index < 0,
- values = one ? null : [],
- max = one ? index + 1 : options.length,
- i = index < 0 ?
- max :
- one ? index : 0;
+ valHooks: {
+ option: {
+ get: function( elem ) {
+ // attributes.value is undefined in Blackberry 4.7 but
+ // uses .value. See #6932
+ var val = elem.attributes.value;
+ return !val || val.specified ? elem.value : elem.text;
+ }
+ },
+ select: {
+ get: function( elem ) {
+ var value, option,
+ options = elem.options,
+ index = elem.selectedIndex,
+ one = elem.type === "select-one" || index < 0,
+ values = one ? null : [],
+ max = one ? index + 1 : options.length,
+ i = index < 0 ?
+ max :
+ one ? index : 0;
- // Loop through all the selected options
- for ( ; i < max; i++ ) {
- option = options[ i ];
+ // Loop through all the selected options
+ for ( ; i < max; i++ ) {
+ option = options[ i ];
- // IE6-9 doesn't update selected after form reset (#2551)
- if ( ( option.selected || i === index ) &&
- // Don't return options that are disabled or in a disabled optgroup
- ( jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null ) &&
- ( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) {
+ // IE6-9 doesn't update selected after form reset (#2551)
+ if ( ( option.selected || i === index ) &&
+ // Don't return options that are disabled or in a disabled optgroup
+ ( jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null ) &&
+ ( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) {
- // Get the specific value for the option
- value = jQuery( option ).val();
+ // Get the specific value for the option
+ value = jQuery( option ).val();
- // We don't need an array for one selects
- if ( one ) {
- return value;
- }
+ // We don't need an array for one selects
+ if ( one ) {
+ return value;
+ }
- // Multi-Selects return an array
- values.push( value );
- }
- }
+ // Multi-Selects return an array
+ values.push( value );
+ }
+ }
- return values;
- },
+ return values;
+ },
- set: function( elem, value ) {
- var optionSet, option,
- options = elem.options,
- values = jQuery.makeArray( value ),
- i = options.length;
+ set: function( elem, value ) {
+ var optionSet, option,
+ options = elem.options,
+ values = jQuery.makeArray( value ),
+ i = options.length;
- while ( i-- ) {
- option = options[ i ];
- if ( (option.selected = jQuery.inArray( jQuery(option).val(), values ) >= 0) ) {
- optionSet = true;
- }
- }
+ while ( i-- ) {
+ option = options[ i ];
+ if ( (option.selected = jQuery.inArray( jQuery(option).val(), values ) >= 0) ) {
+ optionSet = true;
+ }
+ }
- // force browsers to behave consistently when non-matching value is set
- if ( !optionSet ) {
- elem.selectedIndex = -1;
- }
- return values;
- }
- }
- },
+ // force browsers to behave consistently when non-matching value is set
+ if ( !optionSet ) {
+ elem.selectedIndex = -1;
+ }
+ return values;
+ }
+ }
+ },
- attr: function( elem, name, value ) {
- var hooks, ret,
- nType = elem.nodeType;
+ attr: function( elem, name, value ) {
+ var hooks, ret,
+ nType = elem.nodeType;
- // don't get/set attributes on text, comment and attribute nodes
- if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
- return;
- }
+ // don't get/set attributes on text, comment and attribute nodes
+ if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
+ return;
+ }
- // Fallback to prop when attributes are not supported
- if ( typeof elem.getAttribute === core_strundefined ) {
- return jQuery.prop( elem, name, value );
- }
+ // Fallback to prop when attributes are not supported
+ if ( typeof elem.getAttribute === core_strundefined ) {
+ return jQuery.prop( elem, name, value );
+ }
- // All attributes are lowercase
- // Grab necessary hook if one is defined
- if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {
- name = name.toLowerCase();
- hooks = jQuery.attrHooks[ name ] ||
- ( jQuery.expr.match.bool.test( name ) ? boolHook : nodeHook );
- }
+ // All attributes are lowercase
+ // Grab necessary hook if one is defined
+ if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {
+ name = name.toLowerCase();
+ hooks = jQuery.attrHooks[ name ] ||
+ ( jQuery.expr.match.bool.test( name ) ? boolHook : nodeHook );
+ }
- if ( value !== undefined ) {
+ if ( value !== undefined ) {
- if ( value === null ) {
- jQuery.removeAttr( elem, name );
+ if ( value === null ) {
+ jQuery.removeAttr( elem, name );
- } else if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
- return ret;
+ } else if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
+ return ret;
- } else {
- elem.setAttribute( name, value + "" );
- return value;
- }
+ } else {
+ elem.setAttribute( name, value + "" );
+ return value;
+ }
- } else if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
- return ret;
+ } else if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
+ return ret;
- } else {
- ret = jQuery.find.attr( elem, name );
+ } else {
+ ret = jQuery.find.attr( elem, name );
- // Non-existent attributes return null, we normalize to undefined
- return ret == null ?
- undefined :
- ret;
- }
- },
+ // Non-existent attributes return null, we normalize to undefined
+ return ret == null ?
+ undefined :
+ ret;
+ }
+ },
- removeAttr: function( elem, value ) {
- var name, propName,
- i = 0,
- attrNames = value && value.match( core_rnotwhite );
+ removeAttr: function( elem, value ) {
+ var name, propName,
+ i = 0,
+ attrNames = value && value.match( core_rnotwhite );
- if ( attrNames && elem.nodeType === 1 ) {
- while ( (name = attrNames[i++]) ) {
- propName = jQuery.propFix[ name ] || name;
+ if ( attrNames && elem.nodeType === 1 ) {
+ while ( (name = attrNames[i++]) ) {
+ propName = jQuery.propFix[ name ] || name;
- // Boolean attributes get special treatment (#10870)
- if ( jQuery.expr.match.bool.test( name ) ) {
- // Set corresponding property to false
- elem[ propName ] = false;
- }
+ // Boolean attributes get special treatment (#10870)
+ if ( jQuery.expr.match.bool.test( name ) ) {
+ // Set corresponding property to false
+ elem[ propName ] = false;
+ }
- elem.removeAttribute( name );
- }
- }
- },
+ elem.removeAttribute( name );
+ }
+ }
+ },
- attrHooks: {
- type: {
- set: function( elem, value ) {
- if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) {
- // Setting the type on a radio button after the value resets the value in IE6-9
- // Reset value to default in case type is set after value during creation
- var val = elem.value;
- elem.setAttribute( "type", value );
- if ( val ) {
- elem.value = val;
- }
- return value;
- }
- }
- }
- },
+ attrHooks: {
+ type: {
+ set: function( elem, value ) {
+ if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) {
+ // Setting the type on a radio button after the value resets the value in IE6-9
+ // Reset value to default in case type is set after value during creation
+ var val = elem.value;
+ elem.setAttribute( "type", value );
+ if ( val ) {
+ elem.value = val;
+ }
+ return value;
+ }
+ }
+ }
+ },
- propFix: {
- "for": "htmlFor",
- "class": "className"
- },
+ propFix: {
+ "for": "htmlFor",
+ "class": "className"
+ },
- prop: function( elem, name, value ) {
- var ret, hooks, notxml,
- nType = elem.nodeType;
+ prop: function( elem, name, value ) {
+ var ret, hooks, notxml,
+ nType = elem.nodeType;
- // don't get/set properties on text, comment and attribute nodes
- if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
- return;
- }
+ // don't get/set properties on text, comment and attribute nodes
+ if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
+ return;
+ }
- notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
+ notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
- if ( notxml ) {
- // Fix name and attach hooks
- name = jQuery.propFix[ name ] || name;
- hooks = jQuery.propHooks[ name ];
- }
+ if ( notxml ) {
+ // Fix name and attach hooks
+ name = jQuery.propFix[ name ] || name;
+ hooks = jQuery.propHooks[ name ];
+ }
- if ( value !== undefined ) {
- return hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ?
- ret :
- ( elem[ name ] = value );
+ if ( value !== undefined ) {
+ return hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ?
+ ret :
+ ( elem[ name ] = value );
- } else {
- return hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ?
- ret :
- elem[ name ];
- }
- },
+ } else {
+ return hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ?
+ ret :
+ elem[ name ];
+ }
+ },
- propHooks: {
- tabIndex: {
- get: function( elem ) {
- return elem.hasAttribute( "tabindex" ) || rfocusable.test( elem.nodeName ) || elem.href ?
- elem.tabIndex :
- -1;
- }
- }
- }
+ propHooks: {
+ tabIndex: {
+ get: function( elem ) {
+ return elem.hasAttribute( "tabindex" ) || rfocusable.test( elem.nodeName ) || elem.href ?
+ elem.tabIndex :
+ -1;
+ }
+ }
+ }
});
// Hooks for boolean attributes
boolHook = {
- set: function( elem, value, name ) {
- if ( value === false ) {
- // Remove boolean attributes when set to false
- jQuery.removeAttr( elem, name );
- } else {
- elem.setAttribute( name, name );
- }
- return name;
- }
+ set: function( elem, value, name ) {
+ if ( value === false ) {
+ // Remove boolean attributes when set to false
+ jQuery.removeAttr( elem, name );
+ } else {
+ elem.setAttribute( name, name );
+ }
+ return name;
+ }
};
jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name ) {
- var getter = jQuery.expr.attrHandle[ name ] || jQuery.find.attr;
+ var getter = jQuery.expr.attrHandle[ name ] || jQuery.find.attr;
- jQuery.expr.attrHandle[ name ] = function( elem, name, isXML ) {
- var fn = jQuery.expr.attrHandle[ name ],
- ret = isXML ?
- undefined :
- /* jshint eqeqeq: false */
- // Temporarily disable this handler to check existence
- (jQuery.expr.attrHandle[ name ] = undefined) !=
- getter( elem, name, isXML ) ?
+ jQuery.expr.attrHandle[ name ] = function( elem, name, isXML ) {
+ var fn = jQuery.expr.attrHandle[ name ],
+ ret = isXML ?
+ undefined :
+ /* jshint eqeqeq: false */
+ // Temporarily disable this handler to check existence
+ (jQuery.expr.attrHandle[ name ] = undefined) !=
+ getter( elem, name, isXML ) ?
- name.toLowerCase() :
- null;
+ name.toLowerCase() :
+ null;
- // Restore handler
- jQuery.expr.attrHandle[ name ] = fn;
+ // Restore handler
+ jQuery.expr.attrHandle[ name ] = fn;
- return ret;
- };
+ return ret;
+ };
});
// Support: IE9+
// Selectedness for an option in an optgroup can be inaccurate
if ( !jQuery.support.optSelected ) {
- jQuery.propHooks.selected = {
- get: function( elem ) {
- var parent = elem.parentNode;
- if ( parent && parent.parentNode ) {
- parent.parentNode.selectedIndex;
- }
- return null;
- }
- };
+ jQuery.propHooks.selected = {
+ get: function( elem ) {
+ var parent = elem.parentNode;
+ if ( parent && parent.parentNode ) {
+ parent.parentNode.selectedIndex;
+ }
+ return null;
+ }
+ };
}
jQuery.each([
- "tabIndex",
- "readOnly",
- "maxLength",
- "cellSpacing",
- "cellPadding",
- "rowSpan",
- "colSpan",
- "useMap",
- "frameBorder",
- "contentEditable"
+ "tabIndex",
+ "readOnly",
+ "maxLength",
+ "cellSpacing",
+ "cellPadding",
+ "rowSpan",
+ "colSpan",
+ "useMap",
+ "frameBorder",
+ "contentEditable"
], function() {
- jQuery.propFix[ this.toLowerCase() ] = this;
+ jQuery.propFix[ this.toLowerCase() ] = this;
});
// Radios and checkboxes getter/setter
jQuery.each([ "radio", "checkbox" ], function() {
- jQuery.valHooks[ this ] = {
- set: function( elem, value ) {
- if ( jQuery.isArray( value ) ) {
- return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 );
- }
- }
- };
- if ( !jQuery.support.checkOn ) {
- jQuery.valHooks[ this ].get = function( elem ) {
- // Support: Webkit
- // "" is returned instead of "on" if a value isn't specified
- return elem.getAttribute("value") === null ? "on" : elem.value;
- };
- }
+ jQuery.valHooks[ this ] = {
+ set: function( elem, value ) {
+ if ( jQuery.isArray( value ) ) {
+ return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 );
+ }
+ }
+ };
+ if ( !jQuery.support.checkOn ) {
+ jQuery.valHooks[ this ].get = function( elem ) {
+ // Support: Webkit
+ // "" is returned instead of "on" if a value isn't specified
+ return elem.getAttribute("value") === null ? "on" : elem.value;
+ };
+ }
});
var rkeyEvent = /^key/,
- rmouseEvent = /^(?:mouse|contextmenu)|click/,
- rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
- rtypenamespace = /^([^.]*)(?:\.(.+)|)$/;
+ rmouseEvent = /^(?:mouse|contextmenu)|click/,
+ rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
+ rtypenamespace = /^([^.]*)(?:\.(.+)|)$/;
function returnTrue() {
- return true;
+ return true;
}
function returnFalse() {
- return false;
+ return false;
}
function safeActiveElement() {
- try {
- return document.activeElement;
- } catch ( err ) { }
+ try {
+ return document.activeElement;
+ } catch ( err ) { }
}
/*
@@ -4338,1117 +4338,1117 @@ function safeActiveElement() {
*/
jQuery.event = {
- global: {},
-
- add: function( elem, types, handler, data, selector ) {
-
- var handleObjIn, eventHandle, tmp,
- events, t, handleObj,
- special, handlers, type, namespaces, origType,
- elemData = data_priv.get( elem );
-
- // Don't attach events to noData or text/comment nodes (but allow plain objects)
- if ( !elemData ) {
- return;
- }
-
- // Caller can pass in an object of custom data in lieu of the handler
- if ( handler.handler ) {
- handleObjIn = handler;
- handler = handleObjIn.handler;
- selector = handleObjIn.selector;
- }
-
- // Make sure that the handler has a unique ID, used to find/remove it later
- if ( !handler.guid ) {
- handler.guid = jQuery.guid++;
- }
-
- // Init the element's event structure and main handler, if this is the first
- if ( !(events = elemData.events) ) {
- events = elemData.events = {};
- }
- if ( !(eventHandle = elemData.handle) ) {
- eventHandle = elemData.handle = function( e ) {
- // Discard the second event of a jQuery.event.trigger() and
- // when an event is called after a page has unloaded
- return typeof jQuery !== core_strundefined && (!e || jQuery.event.triggered !== e.type) ?
- jQuery.event.dispatch.apply( eventHandle.elem, arguments ) :
- undefined;
- };
- // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events
- eventHandle.elem = elem;
- }
-
- // Handle multiple events separated by a space
- types = ( types || "" ).match( core_rnotwhite ) || [""];
- t = types.length;
- while ( t-- ) {
- tmp = rtypenamespace.exec( types[t] ) || [];
- type = origType = tmp[1];
- namespaces = ( tmp[2] || "" ).split( "." ).sort();
-
- // There *must* be a type, no attaching namespace-only handlers
- if ( !type ) {
- continue;
- }
-
- // If event changes its type, use the special event handlers for the changed type
- special = jQuery.event.special[ type ] || {};
-
- // If selector defined, determine special event api type, otherwise given type
- type = ( selector ? special.delegateType : special.bindType ) || type;
-
- // Update special based on newly reset type
- special = jQuery.event.special[ type ] || {};
-
- // handleObj is passed to all event handlers
- handleObj = jQuery.extend({
- type: type,
- origType: origType,
- data: data,
- handler: handler,
- guid: handler.guid,
- selector: selector,
- needsContext: selector && jQuery.expr.match.needsContext.test( selector ),
- namespace: namespaces.join(".")
- }, handleObjIn );
-
- // Init the event handler queue if we're the first
- if ( !(handlers = events[ type ]) ) {
- handlers = events[ type ] = [];
- handlers.delegateCount = 0;
-
- // Only use addEventListener if the special events handler returns false
- if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
- if ( elem.addEventListener ) {
- elem.addEventListener( type, eventHandle, false );
- }
- }
- }
-
- if ( special.add ) {
- special.add.call( elem, handleObj );
-
- if ( !handleObj.handler.guid ) {
- handleObj.handler.guid = handler.guid;
- }
- }
-
- // Add to the element's handler list, delegates in front
- if ( selector ) {
- handlers.splice( handlers.delegateCount++, 0, handleObj );
- } else {
- handlers.push( handleObj );
- }
-
- // Keep track of which events have ever been used, for event optimization
- jQuery.event.global[ type ] = true;
- }
-
- // Nullify elem to prevent memory leaks in IE
- elem = null;
- },
-
- // Detach an event or set of events from an element
- remove: function( elem, types, handler, selector, mappedTypes ) {
-
- var j, origCount, tmp,
- events, t, handleObj,
- special, handlers, type, namespaces, origType,
- elemData = data_priv.hasData( elem ) && data_priv.get( elem );
-
- if ( !elemData || !(events = elemData.events) ) {
- return;
- }
-
- // Once for each type.namespace in types; type may be omitted
- types = ( types || "" ).match( core_rnotwhite ) || [""];
- t = types.length;
- while ( t-- ) {
- tmp = rtypenamespace.exec( types[t] ) || [];
- type = origType = tmp[1];
- namespaces = ( tmp[2] || "" ).split( "." ).sort();
-
- // Unbind all events (on this namespace, if provided) for the element
- if ( !type ) {
- for ( type in events ) {
- jQuery.event.remove( elem, type + types[ t ], handler, selector, true );
- }
- continue;
- }
-
- special = jQuery.event.special[ type ] || {};
- type = ( selector ? special.delegateType : special.bindType ) || type;
- handlers = events[ type ] || [];
- tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" );
-
- // Remove matching events
- origCount = j = handlers.length;
- while ( j-- ) {
- handleObj = handlers[ j ];
-
- if ( ( mappedTypes || origType === handleObj.origType ) &&
- ( !handler || handler.guid === handleObj.guid ) &&
- ( !tmp || tmp.test( handleObj.namespace ) ) &&
- ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) {
- handlers.splice( j, 1 );
-
- if ( handleObj.selector ) {
- handlers.delegateCount--;
- }
- if ( special.remove ) {
- special.remove.call( elem, handleObj );
- }
- }
- }
-
- // Remove generic event handler if we removed something and no more handlers exist
- // (avoids potential for endless recursion during removal of special event handlers)
- if ( origCount && !handlers.length ) {
- if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) {
- jQuery.removeEvent( elem, type, elemData.handle );
- }
-
- delete events[ type ];
- }
- }
-
- // Remove the expando if it's no longer used
- if ( jQuery.isEmptyObject( events ) ) {
- delete elemData.handle;
- data_priv.remove( elem, "events" );
- }
- },
-
- trigger: function( event, data, elem, onlyHandlers ) {
-
- var i, cur, tmp, bubbleType, ontype, handle, special,
- eventPath = [ elem || document ],
- type = core_hasOwn.call( event, "type" ) ? event.type : event,
- namespaces = core_hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : [];
-
- cur = tmp = elem = elem || document;
-
- // Don't do events on text and comment nodes
- if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
- return;
- }
-
- // focus/blur morphs to focusin/out; ensure we're not firing them right now
- if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
- return;
- }
-
- if ( type.indexOf(".") >= 0 ) {
- // Namespaced trigger; create a regexp to match event type in handle()
- namespaces = type.split(".");
- type = namespaces.shift();
- namespaces.sort();
- }
- ontype = type.indexOf(":") < 0 && "on" + type;
-
- // Caller can pass in a jQuery.Event object, Object, or just an event type string
- event = event[ jQuery.expando ] ?
- event :
- new jQuery.Event( type, typeof event === "object" && event );
-
- // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true)
- event.isTrigger = onlyHandlers ? 2 : 3;
- event.namespace = namespaces.join(".");
- event.namespace_re = event.namespace ?
- new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) :
- null;
-
- // Clean up the event in case it is being reused
- event.result = undefined;
- if ( !event.target ) {
- event.target = elem;
- }
-
- // Clone any incoming data and prepend the event, creating the handler arg list
- data = data == null ?
- [ event ] :
- jQuery.makeArray( data, [ event ] );
-
- // Allow special events to draw outside the lines
- special = jQuery.event.special[ type ] || {};
- if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) {
- return;
- }
-
- // Determine event propagation path in advance, per W3C events spec (#9951)
- // Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
- if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {
-
- bubbleType = special.delegateType || type;
- if ( !rfocusMorph.test( bubbleType + type ) ) {
- cur = cur.parentNode;
- }
- for ( ; cur; cur = cur.parentNode ) {
- eventPath.push( cur );
- tmp = cur;
- }
-
- // Only add window if we got to document (e.g., not plain obj or detached DOM)
- if ( tmp === (elem.ownerDocument || document) ) {
- eventPath.push( tmp.defaultView || tmp.parentWindow || window );
- }
- }
-
- // Fire handlers on the event path
- i = 0;
- while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) {
-
- event.type = i > 1 ?
- bubbleType :
- special.bindType || type;
-
- // jQuery handler
- handle = ( data_priv.get( cur, "events" ) || {} )[ event.type ] && data_priv.get( cur, "handle" );
- if ( handle ) {
- handle.apply( cur, data );
- }
-
- // Native handler
- handle = ontype && cur[ ontype ];
- if ( handle && jQuery.acceptData( cur ) && handle.apply && handle.apply( cur, data ) === false ) {
- event.preventDefault();
- }
- }
- event.type = type;
-
- // If nobody prevented the default action, do it now
- if ( !onlyHandlers && !event.isDefaultPrevented() ) {
-
- if ( (!special._default || special._default.apply( eventPath.pop(), data ) === false) &&
- jQuery.acceptData( elem ) ) {
-
- // Call a native DOM method on the target with the same name name as the event.
- // Don't do default actions on window, that's where global variables be (#6170)
- if ( ontype && jQuery.isFunction( elem[ type ] ) && !jQuery.isWindow( elem ) ) {
-
- // Don't re-trigger an onFOO event when we call its FOO() method
- tmp = elem[ ontype ];
-
- if ( tmp ) {
- elem[ ontype ] = null;
- }
-
- // Prevent re-triggering of the same event, since we already bubbled it above
- jQuery.event.triggered = type;
- elem[ type ]();
- jQuery.event.triggered = undefined;
-
- if ( tmp ) {
- elem[ ontype ] = tmp;
- }
- }
- }
- }
-
- return event.result;
- },
-
- dispatch: function( event ) {
-
- // Make a writable jQuery.Event from the native event object
- event = jQuery.event.fix( event );
-
- var i, j, ret, matched, handleObj,
- handlerQueue = [],
- args = core_slice.call( arguments ),
- handlers = ( data_priv.get( this, "events" ) || {} )[ event.type ] || [],
- special = jQuery.event.special[ event.type ] || {};
-
- // Use the fix-ed jQuery.Event rather than the (read-only) native event
- args[0] = event;
- event.delegateTarget = this;
-
- // Call the preDispatch hook for the mapped type, and let it bail if desired
- if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
- return;
- }
-
- // Determine handlers
- handlerQueue = jQuery.event.handlers.call( this, event, handlers );
-
- // Run delegates first; they may want to stop propagation beneath us
- i = 0;
- while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) {
- event.currentTarget = matched.elem;
-
- j = 0;
- while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) {
-
- // Triggered event must either 1) have no namespace, or
- // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace).
- if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) {
-
- event.handleObj = handleObj;
- event.data = handleObj.data;
-
- ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler )
- .apply( matched.elem, args );
-
- if ( ret !== undefined ) {
- if ( (event.result = ret) === false ) {
- event.preventDefault();
- event.stopPropagation();
- }
- }
- }
- }
- }
-
- // Call the postDispatch hook for the mapped type
- if ( special.postDispatch ) {
- special.postDispatch.call( this, event );
- }
-
- return event.result;
- },
-
- handlers: function( event, handlers ) {
- var i, matches, sel, handleObj,
- handlerQueue = [],
- delegateCount = handlers.delegateCount,
- cur = event.target;
-
- // Find delegate handlers
- // Black-hole SVG