");
289 | $redBtn = $container1.find(".btn.red");
290 | $("body").append($container1);
291 | });
292 |
293 | it("event should be fired when a tree is removed and it contains an element which satisfy the selector", function(done) {
294 | j(document).leave(selector, function() {
295 | expect(this).toBe($redBtn[0]);
296 | done();
297 | });
298 | $(".container2").remove();
299 | });
300 |
301 | it("event should be fired when target element is directly removed from DOM", function(done) {
302 | j(document).leave(selector, function() {
303 | expect(this).toBe($redBtn[0]);
304 | done();
305 | });
306 | $(".btn.red").remove();
307 | });
308 |
309 | });
310 |
311 | describe("Calling arrive function on NodeList and HTMLElement", function() {
312 | it("arrive function should be callable on NodeList", function() {
313 | document.getElementsByTagName("body").arrive(".test", function() {});
314 | expect(true).toBeTruthy();
315 | });
316 |
317 | it("arrive function should be callable on HTMLElement", function() {
318 | document.getElementsByTagName("body")[0].arrive(".test", function() {});
319 | expect(true).toBeTruthy();
320 | });
321 | });
322 | });
323 |
324 | describe("ES2015 arrow function support", function() {
325 | var selector = ".test-elem";
326 | it("Make sure the first argument equals `this` object", function(done) {
327 | var $appendedElem = $("");
328 |
329 | j(document).arrive(selector, function(elem) {
330 | expect(this).toBe(elem);
331 | done();
332 | });
333 | $("body").append($appendedElem);
334 | });
335 |
336 | it("Make sure the first argument equals `this` object with `options.onceOnly` and `options.existing`", function(done) {
337 | j(document).arrive(selector, {onceOnly: true, existing: true}, function(elem) {
338 | expect(this).toBe(elem);
339 | done();
340 | $(selector).remove();
341 | });
342 | });
343 | });
344 | });
345 |
--------------------------------------------------------------------------------
/js/sortablejs/Sortable.min.js:
--------------------------------------------------------------------------------
1 | /*! Sortable 1.7.0 - MIT | git://github.com/rubaxa/Sortable.git */
2 | !function(a){"use strict";"function"==typeof define&&define.amd?define(a):"undefined"!=typeof module&&"undefined"!=typeof module.exports?module.exports=a():window.Sortable=a()}(function(){"use strict";function a(b,c){if(!b||!b.nodeType||1!==b.nodeType)throw"Sortable: `el` must be HTMLElement, and not "+{}.toString.call(b);this.el=b,this.options=c=t({},c),b[V]=this;var d={group:Math.random(),sort:!0,disabled:!1,store:null,handle:null,scroll:!0,scrollSensitivity:30,scrollSpeed:10,draggable:/[uo]l/i.test(b.nodeName)?"li":">*",ghostClass:"sortable-ghost",chosenClass:"sortable-chosen",dragClass:"sortable-drag",ignore:"a, img",filter:null,preventOnFilter:!0,animation:0,setData:function(a,b){a.setData("Text",b.textContent)},dropBubble:!1,dragoverBubble:!1,dataIdAttr:"data-id",delay:0,forceFallback:!1,fallbackClass:"sortable-fallback",fallbackOnBody:!1,fallbackTolerance:0,fallbackOffset:{x:0,y:0},supportPointer:a.supportPointer!==!1};for(var e in d)!(e in c)&&(c[e]=d[e]);ka(c);for(var g in this)"_"===g.charAt(0)&&"function"==typeof this[g]&&(this[g]=this[g].bind(this));this.nativeDraggable=!c.forceFallback&&ca,f(b,"mousedown",this._onTapStart),f(b,"touchstart",this._onTapStart),c.supportPointer&&f(b,"pointerdown",this._onTapStart),this.nativeDraggable&&(f(b,"dragover",this),f(b,"dragenter",this)),ia.push(this._onDragOver),c.store&&this.sort(c.store.get(this))}function b(a,b){"clone"!==a.lastPullMode&&(b=!0),B&&B.state!==b&&(i(B,"display",b?"none":""),b||B.state&&(a.options.group.revertClone?(C.insertBefore(B,D),a._animate(y,B)):C.insertBefore(B,y)),B.state=b)}function c(a,b,c){if(a){c=c||X;do if(">*"===b&&a.parentNode===c||r(a,b))return a;while(a=d(a))}return null}function d(a){var b=a.host;return b&&b.nodeType?b:a.parentNode}function e(a){a.dataTransfer&&(a.dataTransfer.dropEffect="move"),a.preventDefault()}function f(a,b,c){a.addEventListener(b,c,aa)}function g(a,b,c){a.removeEventListener(b,c,aa)}function h(a,b,c){if(a)if(a.classList)a.classList[c?"add":"remove"](b);else{var d=(" "+a.className+" ").replace(T," ").replace(" "+b+" "," ");a.className=(d+(c?" "+b:"")).replace(T," ")}}function i(a,b,c){var d=a&&a.style;if(d){if(void 0===c)return X.defaultView&&X.defaultView.getComputedStyle?c=X.defaultView.getComputedStyle(a,""):a.currentStyle&&(c=a.currentStyle),void 0===b?c:c[b];b in d||(b="-webkit-"+b),d[b]=c+("string"==typeof c?"":"px")}}function j(a,b,c){if(a){var d=a.getElementsByTagName(b),e=0,f=d.length;if(c)for(;e5||b.clientX-(d.left+d.width)>5}function p(a){for(var b=a.tagName+a.className+a.src+a.href+a.textContent,c=b.length,d=0;c--;)d+=b.charCodeAt(c);return d.toString(36)}function q(a,b){var c=0;if(!a||!a.parentNode)return-1;for(;a&&(a=a.previousElementSibling);)"TEMPLATE"===a.nodeName.toUpperCase()||">*"!==b&&!r(a,b)||c++;return c}function r(a,b){if(a){b=b.split(".");var c=b.shift().toUpperCase(),d=new RegExp("\\s("+b.join("|")+")(?=\\s)","g");return!(""!==c&&a.nodeName.toUpperCase()!=c||b.length&&((" "+a.className+" ").match(d)||[]).length!=b.length)}return!1}function s(a,b){var c,d;return function(){void 0===c&&(c=arguments,d=this,Z(function(){1===c.length?a.call(d,c[0]):a.apply(d,c),c=void 0},b))}}function t(a,b){if(a&&b)for(var c in b)b.hasOwnProperty(c)&&(a[c]=b[c]);return a}function u(a){return _&&_.dom?_.dom(a).cloneNode(!0):$?$(a).clone(!0)[0]:a.cloneNode(!0)}function v(a){for(var b=a.getElementsByTagName("input"),c=b.length;c--;){var d=b[c];d.checked&&ha.push(d)}}function w(a){return Z(a,0)}function x(a){return clearTimeout(a)}if("undefined"==typeof window||!window.document)return function(){throw new Error("Sortable.js requires a window with a document")};var y,z,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S={},T=/\s+/g,U=/left|right|inline/,V="Sortable"+(new Date).getTime(),W=window,X=W.document,Y=W.parseInt,Z=W.setTimeout,$=W.jQuery||W.Zepto,_=W.Polymer,aa=!1,ba=!1,ca="draggable"in X.createElement("div"),da=function(a){return!navigator.userAgent.match(/(?:Trident.*rv[ :]?11\.|msie)/i)&&(a=X.createElement("x"),a.style.cssText="pointer-events:auto","auto"===a.style.pointerEvents)}(),ea=!1,fa=Math.abs,ga=Math.min,ha=[],ia=[],ja=s(function(a,b,c){if(c&&b.scroll){var d,e,f,g,h,i,j=c[V],k=b.scrollSensitivity,l=b.scrollSpeed,m=a.clientX,n=a.clientY,o=window.innerWidth,p=window.innerHeight;if(G!==c&&(F=b.scroll,G=c,H=b.scrollFn,F===!0)){F=c;do if(F.offsetWidth-1:e==a)}}var c={},d=a.group;d&&"object"==typeof d||(d={name:d}),c.name=d.name,c.checkPull=b(d.pull,!0),c.checkPut=b(d.put),c.revertClone=d.revertClone,a.group=c};try{window.addEventListener("test",null,Object.defineProperty({},"passive",{get:function(){ba=!1,aa={capture:!1,passive:ba}}}))}catch(a){}return a.prototype={constructor:a,_onTapStart:function(a){var b,d=this,e=this.el,f=this.options,g=f.preventOnFilter,h=a.type,i=a.touches&&a.touches[0],j=(i||a).target,l=a.target.shadowRoot&&a.path&&a.path[0]||j,m=f.filter;if(v(e),!y&&!(/mousedown|pointerdown/.test(h)&&0!==a.button||f.disabled)&&!l.isContentEditable&&(j=c(j,f.draggable,e),j&&E!==j)){if(b=q(j,f.draggable),"function"==typeof m){if(m.call(this,a,j,this))return k(d,l,"filter",j,e,e,b),void(g&&a.preventDefault())}else if(m&&(m=m.split(",").some(function(a){if(a=c(l,a.trim(),e))return k(d,a,"filter",j,e,e,b),!0})))return void(g&&a.preventDefault());f.handle&&!c(l,f.handle,e)||this._prepareDragStart(a,i,j,b)}},_prepareDragStart:function(a,b,c,d){var e,g=this,i=g.el,l=g.options,n=i.ownerDocument;c&&!y&&c.parentNode===i&&(P=a,C=i,y=c,z=y.parentNode,D=y.nextSibling,E=c,N=l.group,L=d,this._lastX=(b||a).clientX,this._lastY=(b||a).clientY,y.style["will-change"]="all",e=function(){g._disableDelayedDrag(),y.draggable=g.nativeDraggable,h(y,l.chosenClass,!0),g._triggerDragStart(a,b),k(g,C,"choose",y,C,C,L)},l.ignore.split(",").forEach(function(a){j(y,a.trim(),m)}),f(n,"mouseup",g._onDrop),f(n,"touchend",g._onDrop),f(n,"touchcancel",g._onDrop),f(n,"selectstart",g),l.supportPointer&&f(n,"pointercancel",g._onDrop),l.delay?(f(n,"mouseup",g._disableDelayedDrag),f(n,"touchend",g._disableDelayedDrag),f(n,"touchcancel",g._disableDelayedDrag),f(n,"mousemove",g._disableDelayedDrag),f(n,"touchmove",g._disableDelayedDrag),l.supportPointer&&f(n,"pointermove",g._disableDelayedDrag),g._dragStartTimer=Z(e,l.delay)):e())},_disableDelayedDrag:function(){var a=this.el.ownerDocument;clearTimeout(this._dragStartTimer),g(a,"mouseup",this._disableDelayedDrag),g(a,"touchend",this._disableDelayedDrag),g(a,"touchcancel",this._disableDelayedDrag),g(a,"mousemove",this._disableDelayedDrag),g(a,"touchmove",this._disableDelayedDrag),g(a,"pointermove",this._disableDelayedDrag)},_triggerDragStart:function(a,b){b=b||("touch"==a.pointerType?a:null),b?(P={target:y,clientX:b.clientX,clientY:b.clientY},this._onDragStart(P,"touch")):this.nativeDraggable?(f(y,"dragend",this),f(C,"dragstart",this._onDragStart)):this._onDragStart(P,!0);try{X.selection?w(function(){X.selection.empty()}):window.getSelection().removeAllRanges()}catch(a){}},_dragStarted:function(){if(C&&y){var b=this.options;h(y,b.ghostClass,!0),h(y,b.dragClass,!1),a.active=this,k(this,C,"start",y,C,C,L)}else this._nulling()},_emulateDragOver:function(){if(Q){if(this._lastX===Q.clientX&&this._lastY===Q.clientY)return;this._lastX=Q.clientX,this._lastY=Q.clientY,da||i(A,"display","none");var a=X.elementFromPoint(Q.clientX,Q.clientY),b=a,c=ia.length;if(a&&a.shadowRoot&&(a=a.shadowRoot.elementFromPoint(Q.clientX,Q.clientY),b=a),b)do{if(b[V]){for(;c--;)ia[c]({clientX:Q.clientX,clientY:Q.clientY,target:a,rootEl:b});break}a=b}while(b=b.parentNode);da||i(A,"display","")}},_onTouchMove:function(b){if(P){var c=this.options,d=c.fallbackTolerance,e=c.fallbackOffset,f=b.touches?b.touches[0]:b,g=f.clientX-P.clientX+e.x,h=f.clientY-P.clientY+e.y,j=b.touches?"translate3d("+g+"px,"+h+"px,0)":"translate("+g+"px,"+h+"px)";if(!a.active){if(d&&ga(fa(f.clientX-this._lastX),fa(f.clientY-this._lastY))y.offsetWidth,x=e.offsetHeight>y.offsetHeight,E=(v?(d.clientX-g.left)/t:(d.clientY-g.top)/u)>.5,F=e.nextElementSibling,G=!1;if(v){var H=y.offsetTop,L=e.offsetTop;G=H===L?e.previousElementSibling===y&&!w||E&&w:e.previousElementSibling===y||y.previousElementSibling===e?(d.clientY-g.top)/u>.5:L>H}else r||(G=F!==y&&!x||E&&x);var M=l(C,j,y,f,e,g,d,G);M!==!1&&(1!==M&&M!==-1||(G=1===M),ea=!0,Z(n,30),b(p,q),y.contains(j)||(G&&!F?j.appendChild(y):e.parentNode.insertBefore(y,G?F:e)),z=y.parentNode,this._animate(f,y),this._animate(g,e))}}},_animate:function(a,b){var c=this.options.animation;if(c){var d=b.getBoundingClientRect();1===a.nodeType&&(a=a.getBoundingClientRect()),i(b,"transition","none"),i(b,"transform","translate3d("+(a.left-d.left)+"px,"+(a.top-d.top)+"px,0)"),b.offsetWidth,i(b,"transition","all "+c+"ms"),i(b,"transform","translate3d(0,0,0)"),clearTimeout(b.animated),b.animated=Z(function(){i(b,"transition",""),i(b,"transform",""),b.animated=!1},c)}},_offUpEvents:function(){var a=this.el.ownerDocument;g(X,"touchmove",this._onTouchMove),g(X,"pointermove",this._onTouchMove),g(a,"mouseup",this._onDrop),g(a,"touchend",this._onDrop),g(a,"pointerup",this._onDrop),g(a,"touchcancel",this._onDrop),g(a,"pointercancel",this._onDrop),g(a,"selectstart",this)},_onDrop:function(b){var c=this.el,d=this.options;clearInterval(this._loopId),clearInterval(S.pid),clearTimeout(this._dragStartTimer),x(this._cloneId),x(this._dragStartId),g(X,"mouseover",this),g(X,"mousemove",this._onTouchMove),this.nativeDraggable&&(g(X,"drop",this),g(c,"dragstart",this._onDragStart)),this._offUpEvents(),b&&(R&&(b.preventDefault(),!d.dropBubble&&b.stopPropagation()),A&&A.parentNode&&A.parentNode.removeChild(A),C!==z&&"clone"===a.active.lastPullMode||B&&B.parentNode&&B.parentNode.removeChild(B),y&&(this.nativeDraggable&&g(y,"dragend",this),m(y),y.style["will-change"]="",h(y,this.options.ghostClass,!1),h(y,this.options.chosenClass,!1),k(this,C,"unchoose",y,z,C,L),C!==z?(M=q(y,d.draggable),M>=0&&(k(null,z,"add",y,z,C,L,M),k(this,C,"remove",y,z,C,L,M),k(null,z,"sort",y,z,C,L,M),k(this,C,"sort",y,z,C,L,M))):y.nextSibling!==D&&(M=q(y,d.draggable),M>=0&&(k(this,C,"update",y,z,C,L,M),k(this,C,"sort",y,z,C,L,M))),a.active&&(null!=M&&M!==-1||(M=L),k(this,C,"end",y,z,C,L,M),this.save()))),this._nulling()},_nulling:function(){C=y=z=A=D=B=E=F=G=P=Q=R=M=I=J=O=N=a.active=null,ha.forEach(function(a){a.checked=!0}),ha.length=0},handleEvent:function(a){switch(a.type){case"drop":case"dragend":this._onDrop(a);break;case"dragover":case"dragenter":y&&(this._onDragOver(a),e(a));break;case"mouseover":this._onDrop(a);break;case"selectstart":a.preventDefault()}},toArray:function(){for(var a,b=[],d=this.el.children,e=0,f=d.length,g=this.options;e 0) {
70 | utils.checkChildNodesRecursively(node.childNodes, registrationData, matchFunc, callbacksToBeCalled);
71 | }
72 | }
73 | },
74 | mergeArrays: function(firstArr, secondArr){
75 | // Overwrites default options with user-defined options.
76 | var options = {},
77 | attrName;
78 | for (attrName in firstArr) {
79 | if (firstArr.hasOwnProperty(attrName)) {
80 | options[attrName] = firstArr[attrName];
81 | }
82 | }
83 | for (attrName in secondArr) {
84 | if (secondArr.hasOwnProperty(attrName)) {
85 | options[attrName] = secondArr[attrName];
86 | }
87 | }
88 | return options;
89 | },
90 | toElementsArray: function (elements) {
91 | // check if object is an array (or array like object)
92 | // Note: window object has .length property but it's not array of elements so don't consider it an array
93 | if (typeof elements !== "undefined" && (typeof elements.length !== "number" || elements === window)) {
94 | elements = [elements];
95 | }
96 | return elements;
97 | }
98 | };
99 | })();
100 |
101 |
102 | // Class to maintain state of all registered events of a single type
103 | var EventsBucket = (function() {
104 | var EventsBucket = function() {
105 | // holds all the events
106 |
107 | this._eventsBucket = [];
108 | // function to be called while adding an event, the function should do the event initialization/registration
109 | this._beforeAdding = null;
110 | // function to be called while removing an event, the function should do the event destruction
111 | this._beforeRemoving = null;
112 | };
113 |
114 | EventsBucket.prototype.addEvent = function(target, selector, options, callback) {
115 | var newEvent = {
116 | target: target,
117 | selector: selector,
118 | options: options,
119 | callback: callback,
120 | firedElems: []
121 | };
122 |
123 | if (this._beforeAdding) {
124 | this._beforeAdding(newEvent);
125 | }
126 |
127 | this._eventsBucket.push(newEvent);
128 | return newEvent;
129 | };
130 |
131 | EventsBucket.prototype.removeEvent = function(compareFunction) {
132 | for (var i=this._eventsBucket.length - 1, registeredEvent; (registeredEvent = this._eventsBucket[i]); i--) {
133 | if (compareFunction(registeredEvent)) {
134 | if (this._beforeRemoving) {
135 | this._beforeRemoving(registeredEvent);
136 | }
137 |
138 | // mark callback as null so that even if an event mutation was already triggered it does not call callback
139 | var removedEvents = this._eventsBucket.splice(i, 1);
140 | if (removedEvents && removedEvents.length) {
141 | removedEvents[0].callback = null;
142 | }
143 | }
144 | }
145 | };
146 |
147 | EventsBucket.prototype.beforeAdding = function(beforeAdding) {
148 | this._beforeAdding = beforeAdding;
149 | };
150 |
151 | EventsBucket.prototype.beforeRemoving = function(beforeRemoving) {
152 | this._beforeRemoving = beforeRemoving;
153 | };
154 |
155 | return EventsBucket;
156 | })();
157 |
158 |
159 | /**
160 | * @constructor
161 | * General class for binding/unbinding arrive and leave events
162 | */
163 | var MutationEvents = function(getObserverConfig, onMutation) {
164 | var eventsBucket = new EventsBucket(),
165 | me = this;
166 |
167 | var defaultOptions = {
168 | fireOnAttributesModification: false
169 | };
170 |
171 | // actual event registration before adding it to bucket
172 | eventsBucket.beforeAdding(function(registrationData) {
173 | var
174 | target = registrationData.target,
175 | observer;
176 |
177 | // mutation observer does not work on window or document
178 | if (target === window.document || target === window) {
179 | target = document.getElementsByTagName("html")[0];
180 | }
181 |
182 | // Create an observer instance
183 | observer = new MutationObserver(function(e) {
184 | onMutation.call(this, e, registrationData);
185 | });
186 |
187 | var config = getObserverConfig(registrationData.options);
188 |
189 | observer.observe(target, config);
190 |
191 | registrationData.observer = observer;
192 | registrationData.me = me;
193 | });
194 |
195 | // cleanup/unregister before removing an event
196 | eventsBucket.beforeRemoving(function (eventData) {
197 | eventData.observer.disconnect();
198 | });
199 |
200 | this.bindEvent = function(selector, options, callback) {
201 | options = utils.mergeArrays(defaultOptions, options);
202 |
203 | var elements = utils.toElementsArray(this);
204 |
205 | for (var i = 0; i < elements.length; i++) {
206 | eventsBucket.addEvent(elements[i], selector, options, callback);
207 | }
208 | };
209 |
210 | this.unbindEvent = function() {
211 | var elements = utils.toElementsArray(this);
212 | eventsBucket.removeEvent(function(eventObj) {
213 | for (var i = 0; i < elements.length; i++) {
214 | if (this === undefined || eventObj.target === elements[i]) {
215 | return true;
216 | }
217 | }
218 | return false;
219 | });
220 | };
221 |
222 | this.unbindEventWithSelectorOrCallback = function(selector) {
223 | var elements = utils.toElementsArray(this),
224 | callback = selector,
225 | compareFunction;
226 |
227 | if (typeof selector === "function") {
228 | compareFunction = function(eventObj) {
229 | for (var i = 0; i < elements.length; i++) {
230 | if ((this === undefined || eventObj.target === elements[i]) && eventObj.callback === callback) {
231 | return true;
232 | }
233 | }
234 | return false;
235 | };
236 | }
237 | else {
238 | compareFunction = function(eventObj) {
239 | for (var i = 0; i < elements.length; i++) {
240 | if ((this === undefined || eventObj.target === elements[i]) && eventObj.selector === selector) {
241 | return true;
242 | }
243 | }
244 | return false;
245 | };
246 | }
247 | eventsBucket.removeEvent(compareFunction);
248 | };
249 |
250 | this.unbindEventWithSelectorAndCallback = function(selector, callback) {
251 | var elements = utils.toElementsArray(this);
252 | eventsBucket.removeEvent(function(eventObj) {
253 | for (var i = 0; i < elements.length; i++) {
254 | if ((this === undefined || eventObj.target === elements[i]) && eventObj.selector === selector && eventObj.callback === callback) {
255 | return true;
256 | }
257 | }
258 | return false;
259 | });
260 | };
261 |
262 | return this;
263 | };
264 |
265 |
266 | /**
267 | * @constructor
268 | * Processes 'arrive' events
269 | */
270 | var ArriveEvents = function() {
271 | // Default options for 'arrive' event
272 | var arriveDefaultOptions = {
273 | fireOnAttributesModification: false,
274 | onceOnly: false,
275 | existing: false
276 | };
277 |
278 | function getArriveObserverConfig(options) {
279 | var config = {
280 | attributes: false,
281 | childList: true,
282 | subtree: true
283 | };
284 |
285 | if (options.fireOnAttributesModification) {
286 | config.attributes = true;
287 | }
288 |
289 | return config;
290 | }
291 |
292 | function onArriveMutation(mutations, registrationData) {
293 | mutations.forEach(function( mutation ) {
294 | var newNodes = mutation.addedNodes,
295 | targetNode = mutation.target,
296 | callbacksToBeCalled = [],
297 | node;
298 |
299 | // If new nodes are added
300 | if( newNodes !== null && newNodes.length > 0 ) {
301 | utils.checkChildNodesRecursively(newNodes, registrationData, nodeMatchFunc, callbacksToBeCalled);
302 | }
303 | else if (mutation.type === "attributes") {
304 | if (nodeMatchFunc(targetNode, registrationData, callbacksToBeCalled)) {
305 | callbacksToBeCalled.push({ callback: registrationData.callback, elem: targetNode });
306 | }
307 | }
308 |
309 | utils.callCallbacks(callbacksToBeCalled, registrationData);
310 | });
311 | }
312 |
313 | function nodeMatchFunc(node, registrationData, callbacksToBeCalled) {
314 | // check a single node to see if it matches the selector
315 | if (utils.matchesSelector(node, registrationData.selector)) {
316 | if(node._id === undefined) {
317 | node._id = arriveUniqueId++;
318 | }
319 | // make sure the arrive event is not already fired for the element
320 | if (registrationData.firedElems.indexOf(node._id) == -1) {
321 | registrationData.firedElems.push(node._id);
322 |
323 | return true;
324 | }
325 | }
326 |
327 | return false;
328 | }
329 |
330 | arriveEvents = new MutationEvents(getArriveObserverConfig, onArriveMutation);
331 |
332 | var mutationBindEvent = arriveEvents.bindEvent;
333 |
334 | // override bindEvent function
335 | arriveEvents.bindEvent = function(selector, options, callback) {
336 |
337 | if (typeof callback === "undefined") {
338 | callback = options;
339 | options = arriveDefaultOptions;
340 | } else {
341 | options = utils.mergeArrays(arriveDefaultOptions, options);
342 | }
343 |
344 | var elements = utils.toElementsArray(this);
345 |
346 | if (options.existing) {
347 | var existing = [];
348 |
349 | for (var i = 0; i < elements.length; i++) {
350 | var nodes = elements[i].querySelectorAll(selector);
351 | for (var j = 0; j < nodes.length; j++) {
352 | existing.push({ callback: callback, elem: nodes[j] });
353 | }
354 | }
355 |
356 | // no need to bind event if the callback has to be fired only once and we have already found the element
357 | if (options.onceOnly && existing.length) {
358 | return callback.call(existing[0].elem, existing[0].elem);
359 | }
360 |
361 | setTimeout(utils.callCallbacks, 1, existing);
362 | }
363 |
364 | mutationBindEvent.call(this, selector, options, callback);
365 | };
366 |
367 | return arriveEvents;
368 | };
369 |
370 |
371 | /**
372 | * @constructor
373 | * Processes 'leave' events
374 | */
375 | var LeaveEvents = function() {
376 | // Default options for 'leave' event
377 | var leaveDefaultOptions = {};
378 |
379 | function getLeaveObserverConfig() {
380 | var config = {
381 | childList: true,
382 | subtree: true
383 | };
384 |
385 | return config;
386 | }
387 |
388 | function onLeaveMutation(mutations, registrationData) {
389 | mutations.forEach(function( mutation ) {
390 | var removedNodes = mutation.removedNodes,
391 | callbacksToBeCalled = [];
392 |
393 | if( removedNodes !== null && removedNodes.length > 0 ) {
394 | utils.checkChildNodesRecursively(removedNodes, registrationData, nodeMatchFunc, callbacksToBeCalled);
395 | }
396 |
397 | utils.callCallbacks(callbacksToBeCalled, registrationData);
398 | });
399 | }
400 |
401 | function nodeMatchFunc(node, registrationData) {
402 | return utils.matchesSelector(node, registrationData.selector);
403 | }
404 |
405 | leaveEvents = new MutationEvents(getLeaveObserverConfig, onLeaveMutation);
406 |
407 | var mutationBindEvent = leaveEvents.bindEvent;
408 |
409 | // override bindEvent function
410 | leaveEvents.bindEvent = function(selector, options, callback) {
411 |
412 | if (typeof callback === "undefined") {
413 | callback = options;
414 | options = leaveDefaultOptions;
415 | } else {
416 | options = utils.mergeArrays(leaveDefaultOptions, options);
417 | }
418 |
419 | mutationBindEvent.call(this, selector, options, callback);
420 | };
421 |
422 | return leaveEvents;
423 | };
424 |
425 |
426 | var arriveEvents = new ArriveEvents(),
427 | leaveEvents = new LeaveEvents();
428 |
429 | function exposeUnbindApi(eventObj, exposeTo, funcName) {
430 | // expose unbind function with function overriding
431 | utils.addMethod(exposeTo, funcName, eventObj.unbindEvent);
432 | utils.addMethod(exposeTo, funcName, eventObj.unbindEventWithSelectorOrCallback);
433 | utils.addMethod(exposeTo, funcName, eventObj.unbindEventWithSelectorAndCallback);
434 | }
435 |
436 | /*** expose APIs ***/
437 | function exposeApi(exposeTo) {
438 | exposeTo.arrive = arriveEvents.bindEvent;
439 | exposeUnbindApi(arriveEvents, exposeTo, "unbindArrive");
440 |
441 | exposeTo.leave = leaveEvents.bindEvent;
442 | exposeUnbindApi(leaveEvents, exposeTo, "unbindLeave");
443 | }
444 |
445 | if ($) {
446 | exposeApi($.fn);
447 | }
448 | exposeApi(HTMLElement.prototype);
449 | exposeApi(NodeList.prototype);
450 | exposeApi(HTMLCollection.prototype);
451 | exposeApi(HTMLDocument.prototype);
452 | exposeApi(Window.prototype);
453 |
454 | var Arrive = {};
455 | // expose functions to unbind all arrive/leave events
456 | exposeUnbindApi(arriveEvents, Arrive, "unbindAllArrive");
457 | exposeUnbindApi(leaveEvents, Arrive, "unbindAllLeave");
458 |
459 | return Arrive;
460 |
461 | })(window, typeof jQuery === 'undefined' ? null : jQuery, undefined);
--------------------------------------------------------------------------------
/js/sortablejs/README.md:
--------------------------------------------------------------------------------
1 | # Sortable
2 | Sortable is a minimalist JavaScript library for reorderable drag-and-drop lists.
3 |
4 | Demo: http://rubaxa.github.io/Sortable/
5 |
6 |
7 | ## Features
8 |
9 | * Supports touch devices and [modern](http://caniuse.com/#search=drag) browsers (including IE9)
10 | * Can drag from one list to another or within the same list
11 | * CSS animation when moving items
12 | * Supports drag handles *and selectable text* (better than voidberg's html5sortable)
13 | * Smart auto-scrolling
14 | * Built using native HTML5 drag and drop API
15 | * Supports
16 | * [Meteor](https://github.com/SortableJS/meteor-sortablejs)
17 | * AngularJS
18 | * [2.0+](https://github.com/SortableJS/angular-sortablejs)
19 | * [1.*](https://github.com/SortableJS/angular-legacy-sortablejs)
20 | * React
21 | * [ES2015+](https://github.com/SortableJS/react-sortablejs)
22 | * [Mixin](https://github.com/SortableJS/react-mixin-sortablejs)
23 | * [Knockout](https://github.com/SortableJS/knockout-sortablejs)
24 | * [Polymer](https://github.com/SortableJS/polymer-sortablejs)
25 | * [Vue](https://github.com/SortableJS/Vue.Draggable)
26 | * Supports any CSS library, e.g. [Bootstrap](#bs)
27 | * Simple API
28 | * [CDN](#cdn)
29 | * No jQuery (but there is [support](#jq))
30 |
31 |
32 |
33 |
34 |
35 | ### Articles
36 |
37 | * [Sortable v1.0 — New capabilities](https://github.com/RubaXa/Sortable/wiki/Sortable-v1.0-—-New-capabilities/) (December 22, 2014)
38 | * [Sorting with the help of HTML5 Drag'n'Drop API](https://github.com/RubaXa/Sortable/wiki/Sorting-with-the-help-of-HTML5-Drag'n'Drop-API/) (December 23, 2013)
39 |
40 |
41 |
42 |
43 | ### Install
44 |
45 | Via npm
46 |
47 | ```bash
48 | $ npm install sortablejs --save
49 | ```
50 |
51 | Via bower:
52 |
53 | ```bash
54 | $ bower install --save sortablejs
55 | ```
56 |
57 |
58 |
59 | ### Usage
60 | ```html
61 |
62 |
item 1
63 |
item 2
64 |
item 3
65 |
66 | ```
67 |
68 | ```js
69 | var el = document.getElementById('items');
70 | var sortable = Sortable.create(el);
71 | ```
72 |
73 | You can use any element for the list and its elements, not just `ul`/`li`. Here is an [example with `div`s](http://jsbin.com/qumuwe/edit?html,js,output).
74 |
75 |
76 | ---
77 |
78 |
79 | ### Options
80 | ```js
81 | var sortable = new Sortable(el, {
82 | group: "name", // or { name: "...", pull: [true, false, clone], put: [true, false, array] }
83 | sort: true, // sorting inside list
84 | delay: 0, // time in milliseconds to define when the sorting should start
85 | disabled: false, // Disables the sortable if set to true.
86 | store: null, // @see Store
87 | animation: 150, // ms, animation speed moving items when sorting, `0` — without animation
88 | handle: ".my-handle", // Drag handle selector within list items
89 | filter: ".ignore-elements", // Selectors that do not lead to dragging (String or Function)
90 | preventOnFilter: true, // Call `event.preventDefault()` when triggered `filter`
91 | draggable: ".item", // Specifies which items inside the element should be draggable
92 | ghostClass: "sortable-ghost", // Class name for the drop placeholder
93 | chosenClass: "sortable-chosen", // Class name for the chosen item
94 | dragClass: "sortable-drag", // Class name for the dragging item
95 | dataIdAttr: 'data-id',
96 |
97 | forceFallback: false, // ignore the HTML5 DnD behaviour and force the fallback to kick in
98 |
99 | fallbackClass: "sortable-fallback", // Class name for the cloned DOM Element when using forceFallback
100 | fallbackOnBody: false, // Appends the cloned DOM Element into the Document's Body
101 | fallbackTolerance: 0, // Specify in pixels how far the mouse should move before it's considered as a drag.
102 |
103 | scroll: true, // or HTMLElement
104 | scrollFn: function(offsetX, offsetY, originalEvent) { ... }, // if you have custom scrollbar scrollFn may be used for autoscrolling
105 | scrollSensitivity: 30, // px, how near the mouse must be to an edge to start scrolling.
106 | scrollSpeed: 10, // px
107 |
108 | setData: function (/** DataTransfer */dataTransfer, /** HTMLElement*/dragEl) {
109 | dataTransfer.setData('Text', dragEl.textContent); // `dataTransfer` object of HTML5 DragEvent
110 | },
111 |
112 | // Element is chosen
113 | onChoose: function (/**Event*/evt) {
114 | evt.oldIndex; // element index within parent
115 | },
116 |
117 | // Element dragging started
118 | onStart: function (/**Event*/evt) {
119 | evt.oldIndex; // element index within parent
120 | },
121 |
122 | // Element dragging ended
123 | onEnd: function (/**Event*/evt) {
124 | var itemEl = evt.item; // dragged HTMLElement
125 | evt.to; // target list
126 | evt.from; // previous list
127 | evt.oldIndex; // element's old index within old parent
128 | evt.newIndex; // element's new index within new parent
129 | },
130 |
131 | // Element is dropped into the list from another list
132 | onAdd: function (/**Event*/evt) {
133 | // same properties as onEnd
134 | },
135 |
136 | // Changed sorting within list
137 | onUpdate: function (/**Event*/evt) {
138 | // same properties as onEnd
139 | },
140 |
141 | // Called by any change to the list (add / update / remove)
142 | onSort: function (/**Event*/evt) {
143 | // same properties as onEnd
144 | },
145 |
146 | // Element is removed from the list into another list
147 | onRemove: function (/**Event*/evt) {
148 | // same properties as onEnd
149 | },
150 |
151 | // Attempt to drag a filtered element
152 | onFilter: function (/**Event*/evt) {
153 | var itemEl = evt.item; // HTMLElement receiving the `mousedown|tapstart` event.
154 | },
155 |
156 | // Event when you move an item in the list or between lists
157 | onMove: function (/**Event*/evt, /**Event*/originalEvent) {
158 | // Example: http://jsbin.com/tuyafe/1/edit?js,output
159 | evt.dragged; // dragged HTMLElement
160 | evt.draggedRect; // TextRectangle {left, top, right и bottom}
161 | evt.related; // HTMLElement on which have guided
162 | evt.relatedRect; // TextRectangle
163 | originalEvent.clientY; // mouse position
164 | // return false; — for cancel
165 | },
166 |
167 | // Called when creating a clone of element
168 | onClone: function (/**Event*/evt) {
169 | var origEl = evt.item;
170 | var cloneEl = evt.clone;
171 | }
172 | });
173 | ```
174 |
175 |
176 | ---
177 |
178 |
179 | #### `group` option
180 | To drag elements from one list into another, both lists must have the same `group` value.
181 | You can also define whether lists can give away, give and keep a copy (`clone`), and receive elements.
182 |
183 | * name: `String` — group name
184 | * pull: `true|false|'clone'|function` — ability to move from the list. `clone` — copy the item, rather than move.
185 | * put: `true|false|["foo", "bar"]|function` — whether elements can be added from other lists, or an array of group names from which elements can be taken.
186 | * revertClone: `boolean` — revert cloned element to initial position after moving to a another list.
187 |
188 |
189 | Demo:
190 | - http://jsbin.com/naduvo/edit?js,output
191 | - http://jsbin.com/rusuvot/edit?js,output — use of complex logic in the `pull` and` put`
192 | - http://jsbin.com/magogub/edit?js,output — use `revertClone: true`
193 |
194 |
195 | ---
196 |
197 |
198 | #### `sort` option
199 | Sorting inside list.
200 |
201 | Demo: http://jsbin.com/videzob/edit?html,js,output
202 |
203 |
204 | ---
205 |
206 |
207 | #### `delay` option
208 | Time in milliseconds to define when the sorting should start.
209 |
210 | Demo: http://jsbin.com/xizeh/edit?html,js,output
211 |
212 |
213 | ---
214 |
215 |
216 | #### `disabled` options
217 | Disables the sortable if set to `true`.
218 |
219 | Demo: http://jsbin.com/xiloqu/edit?html,js,output
220 |
221 | ```js
222 | var sortable = Sortable.create(list);
223 |
224 | document.getElementById("switcher").onclick = function () {
225 | var state = sortable.option("disabled"); // get
226 |
227 | sortable.option("disabled", !state); // set
228 | };
229 | ```
230 |
231 |
232 | ---
233 |
234 |
235 | #### `handle` option
236 | To make list items draggable, Sortable disables text selection by the user.
237 | That's not always desirable. To allow text selection, define a drag handler,
238 | which is an area of every list element that allows it to be dragged around.
239 |
240 | Demo: http://jsbin.com/newize/edit?html,js,output
241 |
242 | ```js
243 | Sortable.create(el, {
244 | handle: ".my-handle"
245 | });
246 | ```
247 |
248 | ```html
249 |
250 |
:: list item text one
251 |
:: list item text two
252 |
253 | ```
254 |
255 | ```css
256 | .my-handle {
257 | cursor: move;
258 | cursor: -webkit-grabbing;
259 | }
260 | ```
261 |
262 |
263 | ---
264 |
265 |
266 | #### `filter` option
267 |
268 |
269 | ```js
270 | Sortable.create(list, {
271 | filter: ".js-remove, .js-edit",
272 | onFilter: function (evt) {
273 | var item = evt.item,
274 | ctrl = evt.target;
275 |
276 | if (Sortable.utils.is(ctrl, ".js-remove")) { // Click on remove button
277 | item.parentNode.removeChild(item); // remove sortable item
278 | }
279 | else if (Sortable.utils.is(ctrl, ".js-edit")) { // Click on edit link
280 | // ...
281 | }
282 | }
283 | })
284 | ```
285 |
286 |
287 | ---
288 |
289 |
290 | #### `ghostClass` option
291 | Class name for the drop placeholder (default `sortable-ghost`).
292 |
293 | Demo: http://jsbin.com/hunifu/4/edit?css,js,output
294 |
295 | ```css
296 | .ghost {
297 | opacity: 0.4;
298 | }
299 | ```
300 |
301 | ```js
302 | Sortable.create(list, {
303 | ghostClass: "ghost"
304 | });
305 | ```
306 |
307 |
308 | ---
309 |
310 |
311 | #### `chosenClass` option
312 | Class name for the chosen item (default `sortable-chosen`).
313 |
314 | Demo: http://jsbin.com/hunifu/3/edit?html,css,js,output
315 |
316 | ```css
317 | .chosen {
318 | color: #fff;
319 | background-color: #c00;
320 | }
321 | ```
322 |
323 | ```js
324 | Sortable.create(list, {
325 | delay: 500,
326 | chosenClass: "chosen"
327 | });
328 | ```
329 |
330 |
331 | ---
332 |
333 |
334 | #### `forceFallback` option
335 | If set to `true`, the Fallback for non HTML5 Browser will be used, even if we are using an HTML5 Browser.
336 | This gives us the possibility to test the behaviour for older Browsers even in newer Browser, or make the Drag 'n Drop feel more consistent between Desktop , Mobile and old Browsers.
337 |
338 | On top of that, the Fallback always generates a copy of that DOM Element and appends the class `fallbackClass` defined in the options. This behaviour controls the look of this 'dragged' Element.
339 |
340 | Demo: http://jsbin.com/yacuqib/edit?html,css,js,output
341 |
342 |
343 | ---
344 |
345 |
346 | #### `fallbackTolerance` option
347 | Emulates the native drag threshold. Specify in pixels how far the mouse should move before it's considered as a drag.
348 | Useful if the items are also clickable like in a list of links.
349 |
350 | When the user clicks inside a sortable element, it's not uncommon for your hand to move a little between the time you press and the time you release.
351 | Dragging only starts if you move the pointer past a certain tolerance, so that you don't accidentally start dragging every time you click.
352 |
353 | 3 to 5 are probably good values.
354 |
355 |
356 | ---
357 |
358 |
359 | #### `scroll` option
360 | If set to `true`, the page (or sortable-area) scrolls when coming to an edge.
361 |
362 | Demo:
363 | - `window`: http://jsbin.com/tutuzeh/edit?html,js,output
364 | - `overflow: hidden`: http://jsbin.com/kolisu/edit?html,js,output
365 |
366 |
367 | ---
368 |
369 |
370 | #### `scrollFn` option
371 | Defines function that will be used for autoscrolling. el.scrollTop/el.scrollLeft is used by default.
372 | Useful when you have custom scrollbar with dedicated scroll function.
373 |
374 |
375 | ---
376 |
377 |
378 | #### `scrollSensitivity` option
379 | Defines how near the mouse must be to an edge to start scrolling.
380 |
381 |
382 | ---
383 |
384 |
385 | #### `scrollSpeed` option
386 | The speed at which the window should scroll once the mouse pointer gets within the `scrollSensitivity` distance.
387 |
388 |
389 | ---
390 |
391 |
392 | ### Event object ([demo](http://jsbin.com/xedusu/edit?js,output))
393 |
394 | - to:`HTMLElement` — list, in which moved element.
395 | - from:`HTMLElement` — previous list
396 | - item:`HTMLElement` — dragged element
397 | - clone:`HTMLElement`
398 | - oldIndex:`Number|undefined` — old index within parent
399 | - newIndex:`Number|undefined` — new index within parent
400 |
401 |
402 | #### `move` event object
403 | - to:`HTMLElement`
404 | - from:`HTMLElement`
405 | - dragged:`HTMLElement`
406 | - draggedRect:` TextRectangle`
407 | - related:`HTMLElement` — element on which have guided
408 | - relatedRect:` TextRectangle`
409 |
410 |
411 | ---
412 |
413 |
414 | ### Method
415 |
416 |
417 | ##### option(name:`String`[, value:`*`]):`*`
418 | Get or set the option.
419 |
420 |
421 |
422 | ##### closest(el:`String`[, selector:`HTMLElement`]):`HTMLElement|null`
423 | For each element in the set, get the first element that matches the selector by testing the element itself and traversing up through its ancestors in the DOM tree.
424 |
425 |
426 | ##### toArray():`String[]`
427 | Serializes the sortable's item `data-id`'s (`dataIdAttr` option) into an array of string.
428 |
429 |
430 | ##### sort(order:`String[]`)
431 | Sorts the elements according to the array.
432 |
433 | ```js
434 | var order = sortable.toArray();
435 | sortable.sort(order.reverse()); // apply
436 | ```
437 |
438 |
439 | ##### save()
440 | Save the current sorting (see [store](#store))
441 |
442 |
443 | ##### destroy()
444 | Removes the sortable functionality completely.
445 |
446 |
447 | ---
448 |
449 |
450 |
451 | ### Store
452 | Saving and restoring of the sort.
453 |
454 | ```html
455 |
456 |
order
457 |
save
458 |
restore
459 |
460 | ```
461 |
462 | ```js
463 | Sortable.create(el, {
464 | group: "localStorage-example",
465 | store: {
466 | /**
467 | * Get the order of elements. Called once during initialization.
468 | * @param {Sortable} sortable
469 | * @returns {Array}
470 | */
471 | get: function (sortable) {
472 | var order = localStorage.getItem(sortable.options.group.name);
473 | return order ? order.split('|') : [];
474 | },
475 |
476 | /**
477 | * Save the order of elements. Called onEnd (when the item is dropped).
478 | * @param {Sortable} sortable
479 | */
480 | set: function (sortable) {
481 | var order = sortable.toArray();
482 | localStorage.setItem(sortable.options.group.name, order.join('|'));
483 | }
484 | }
485 | })
486 | ```
487 |
488 |
489 | ---
490 |
491 |
492 |
493 | ### Bootstrap
494 | Demo: http://jsbin.com/qumuwe/edit?html,js,output
495 |
496 | ```html
497 |
498 |
499 |
500 |
501 |
502 |
503 |
504 |
505 |
506 |