UNPKG

can

Version:

MIT-licensed, client-side, JavaScript framework that makes building rich web applications easy.

405 lines (395 loc) 11.3 kB
steal('can/util/can.js', 'can/util/attr', 'mootools', 'can/event', 'can/util/fragment.js', 'can/util/deferred.js', 'can/util/array/each.js', 'can/util/object/isplain', "can/util/inserted", function (can, attr) { /* jshint maxdepth:5 */ // mootools.js // --------- // _MooTools node list._ // // Map string helpers. can.trim = function (s) { return s ? s.trim() : s; }; // This extend() function is ruthlessly and shamelessly stolen from // jQuery 1.8.2:, lines 291-353. var extend = function () { 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 case when target is a string or something (possible in deep copy) if (typeof target !== 'object' && !can.isFunction(target)) { target = {}; } // 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]; // Prevent never-ending loop if (target === copy) { continue; } // Recurse if we're merging plain objects or arrays if (deep && copy && (can.isPlainObject(copy) || (copyIsArray = can.isArray(copy)))) { if (copyIsArray) { copyIsArray = false; clone = src && can.isArray(src) ? src : []; } else { clone = src && can.isPlainObject(src) ? src : {}; } // Never move original objects, clone them target[name] = can.extend(deep, clone, copy); // Don't bring in undefined values } else if (copy !== undefined) { target[name] = copy; } } } } // Return the modified object return target; }; can.extend = extend; // Map array helpers. can.makeArray = function (item) { // All other libraries return a copy if item is an array. // The original Mootools Array.from returned the same item so we need to slightly modify it if (item === null) { return []; } try { return Type.isEnumerable(item) && typeof item !== 'string' ? Array.prototype.slice.call(item) : [item]; } catch (ex) { // some things like DOMNodeChildCollections don't slice so good. // This pains me, but it has to be done. var arr = [], i; for (i = 0; i < item.length; ++i) { arr.push(item[i]); } return arr; } }; can.isArray = function (arr) { return typeOf(arr) === 'array'; }; can.inArray = function (item, arr, fromIndex) { if (!arr) { return -1; } return Array.prototype.indexOf.call(arr, item, fromIndex); }; can.map = function (arr, fn) { return Array.from(arr || []) .map(fn); }; // Map object helpers. can.param = function (object) { return Object.toQueryString(object); }; can.isEmptyObject = function (object) { return Object.keys(object) .length === 0; }; can.isFunction = function (f) { return typeOf(f) === 'function'; }; // Make this object so you can bind on it. can.bind = function (ev, cb) { // If we can bind to it... if (this.bind && this.bind !== can.bind) { this.bind(ev, cb); } else if (this.nodeName && (this.nodeType && this.nodeType !== 11)) { can.$(this) .addEvent(ev, cb); } else if (this.addEvent) { this.addEvent(ev, cb); } else { // Make it bind-able... can.addEvent.call(this, ev, cb); } return this; }; can.unbind = function (ev, cb) { // If we can bind to it... if (this.unbind && this.unbind !== can.unbind) { this.unbind(ev, cb); } else if (this.nodeName && (this.nodeType && this.nodeType !== 11)) { can.$(this) .removeEvent(ev, cb); } else if (this.removeEvent) { this.removeEvent(ev, cb); } else { // Make it bind-able... can.removeEvent.call(this, ev, cb); } return this; }; // Alias on/off to bind/unbind respectively can.on = can.bind; can.off = can.unbind; can.trigger = function (item, event, args, bubble) { // Defaults to `true`. bubble = bubble === undefined ? true : bubble; args = args || []; var propagating = true; if (item.fireEvent) { item = item[0] || item; // walk up parents to simulate bubbling . while (item && propagating) { // Handle walking yourself. if (!event.type) { event = { type: event, target: item, stopPropagation: function () { propagating = false; } }; } var events = item !== window ? can.$(item) .retrieve('events')[0] : item.retrieve('events'); if (events && events[event.type]) { events[event.type].keys.each(function (fn) { fn.apply(item, [event].concat(args)); }, this); } // If we are bubbling, get parent node. if (bubble && item.parentNode && item.parentNode.nodeType !== 11) { item = item.parentNode; } else { item = null; } } } else { if (typeof event === 'string') { event = { type: event }; } event.target = event.target || item; can.dispatch.call(item, event, can.makeArray(args)); } }; can.delegate = function (selector, ev, cb) { if (this.delegate) { this.delegate(selector, ev, cb); } else if (this.addEvent) { this.addEvent(ev + ':relay(' + selector + ')', cb); } else { // make it bind-able ... can.bind.call(this, ev, cb); } return this; }; can.undelegate = function (selector, ev, cb) { if (this.undelegate) { this.undelegate(selector, ev, cb); } else if (this.removeEvent) { this.removeEvent(ev + ':relay(' + selector + ')', cb); } else { can.unbind.call(this, ev, cb); } return this; }; var optionsMap = { type: 'method', success: undefined, error: undefined }; var updateDeferred = function (xhr, d) { for (var prop in xhr) { if (typeof d[prop] === 'function') { d[prop] = function () { xhr[prop].apply(xhr, arguments); }; } else { d[prop] = prop[xhr]; } } }; can.ajax = function (options) { var d = can.Deferred(), requestOptions = can.extend({}, options), request; // Map jQuery options to MooTools options. for (var option in optionsMap) { if (requestOptions[option] !== undefined) { requestOptions[optionsMap[option]] = requestOptions[option]; delete requestOptions[option]; } } // Mootools defaults to 'post', but Can expects a default of 'get' requestOptions.method = requestOptions.method || 'get'; requestOptions.url = requestOptions.url.toString(); var success = options.onSuccess || options.success, error = options.onFailure || options.error; requestOptions.onSuccess = function (response, xml) { var data = response; updateDeferred(request.xhr, d); d.resolve(data, 'success', request.xhr); if (success) { success(data, 'success', request.xhr); } }; requestOptions.onFailure = function () { updateDeferred(request.xhr, d); d.reject(request.xhr, 'error'); if (error) { error(request.xhr, 'error'); } }; if (options.dataType === 'json') { request = new Request.JSON(requestOptions); } else { request = new Request(requestOptions); } request.send(); updateDeferred(request.xhr, d); return d; }; // Element -- get the wrapped helper. can.$ = function (selector) { if (selector === window) { return window; } return $$(selector && selector.nodeName ? [selector] : selector); }; // Add `document` fragment support. var old = document.id; document.id = function (el) { if (el && el.nodeType === 11) { return el; } else { return old.apply(document, arguments); } }; can.append = function (wrapped, html) { if (typeof html === 'string') { html = can.buildFragment(html); } return wrapped.grab(html); }; can.filter = function (wrapped, filter) { return wrapped.filter(filter); }; can.data = function (wrapped, key, value) { if (value === undefined) { return wrapped[0].retrieve(key); } else { return wrapped.store(key, value); } }; can.addClass = function (wrapped, className) { return wrapped.addClass(className); }; can.remove = function (wrapped) { // We need to remove text nodes ourselves. var filtered = wrapped.filter(function (node) { if (node.nodeType !== 1) { node.parentNode.removeChild(node); } else { return true; } }); filtered.destroy(); return filtered; }; can.has = function (wrapped, element) { // this way work in mootools if (Slick.contains(wrapped[0], element)) { return wrapped; } else { return []; } }; // Destroyed method. var destroy = Element.prototype.destroy, grab = Element.prototype.grab, oldSet = Element.prototype.set; Element.implement({ destroy: function () { can.trigger(this, 'removed', [], false); var elems = this.getElementsByTagName('*'); for (var i = 0, elem; (elem = elems[i]) !== undefined; i++) { can.trigger(elem, 'removed', [], false); } destroy.apply(this, arguments); }, grab: function (el) { var elems; if (el && el.nodeType === 11) { elems = can.makeArray(el.childNodes); } else { elems = [el]; } var ret = grab.apply(this, arguments); can.inserted(elems); return ret; }, set: function (attrName, value) { var isAttributeOrProp = can.inArray(attrName, ["events", "html", "load", "morph", "send", "tag", "tween"]) === -1, newValue, oldValue; if (isAttributeOrProp) { oldValue = this.get(attrName); } var res = oldSet.apply(this, arguments); if (isAttributeOrProp) { newValue = this.get(attrName); } if (newValue !== oldValue) { can.attr.trigger(this, attrName, oldValue); } return res; }.overloadSetter() }); can.get = function (wrapped, index) { return wrapped[index]; }; // Overwrite to handle IE not having an id. // IE barfs if text node. var idOf = Slick.uidOf; Slick.uidOf = function (node) { // for some reason, in IE8, node will be the window but not equal it. if (node.nodeType === 1 || node === window || node.document === document) { return idOf(node); } else { return Math.random(); } }; Element.NativeEvents.hashchange = 2; // Setup attributes events can.attr = attr; // turn off mutation events for zepto delete attr.MutationObserver; Element.Events.attributes = { onAdd: function () { var el = can.$(this); can.data(el, "canHasAttributesBindings", (can.data(el, "canHasAttributesBindings") || 0) + 1); }, onRemove: function () { var el = can.$(this), cur = can.data(el, "canHasAttributesBindings") || 0; if (cur <= 0) { can.cleanData(el, "canHasAttributesBindings"); } else { can.data(el, "canHasAttributesBindings", cur - 1); } } }; return can; });