can
Version:
MIT-licensed, client-side, JavaScript framework that makes building rich web applications easy.
212 lines (211 loc) • 7.87 kB
JavaScript
/*!
* CanJS - 2.3.34
* http://canjs.com/
* Copyright (c) 2018 Bitovi
* Mon, 30 Apr 2018 20:56:51 GMT
* Licensed MIT
*/
/*can@2.3.34#control/control*/
define([
'can/util/library',
'can/construct'
], function (can) {
var bind = function (el, ev, callback) {
can.bind.call(el, ev, callback);
return function () {
can.unbind.call(el, ev, callback);
};
}, isFunction = can.isFunction, extend = can.extend, each = can.each, slice = [].slice, paramReplacer = /\{([^\}]+)\}/g, special = can.getObject('$.event.special', [can]) || {}, delegate = function (el, selector, ev, callback) {
can.delegate.call(el, selector, ev, callback);
return function () {
can.undelegate.call(el, selector, ev, callback);
};
}, binder = function (el, ev, callback, selector) {
return selector ? delegate(el, can.trim(selector), ev, callback) : bind(el, ev, callback);
}, basicProcessor;
var Control = can.Control = can.Construct({
setup: function () {
can.Construct.setup.apply(this, arguments);
if (can.Control) {
var control = this, funcName;
control.actions = {};
for (funcName in control.prototype) {
if (control._isAction(funcName)) {
control.actions[funcName] = control._action(funcName);
}
}
}
},
_shifter: function (context, name) {
var method = typeof name === 'string' ? context[name] : name;
if (!isFunction(method)) {
method = context[method];
}
return function () {
context.called = name;
return method.apply(context, [this.nodeName ? can.$(this) : this].concat(slice.call(arguments, 0)));
};
},
_isAction: function (methodName) {
var val = this.prototype[methodName], type = typeof val;
return methodName !== 'constructor' && (type === 'function' || type === 'string' && isFunction(this.prototype[val])) && !!(special[methodName] || processors[methodName] || /[^\w]/.test(methodName));
},
_action: function (methodName, options) {
paramReplacer.lastIndex = 0;
if (options || !paramReplacer.test(methodName)) {
var convertedName = options ? can.sub(methodName, this._lookup(options)) : methodName;
if (!convertedName) {
can.dev.log('can/control/control.js: No property found for handling ' + methodName);
return null;
}
var arr = can.isArray(convertedName), name = arr ? convertedName[1] : convertedName, parts = name.split(/\s+/g), event = parts.pop();
return {
processor: processors[event] || basicProcessor,
parts: [
name,
parts.join(' '),
event
],
delegate: arr ? convertedName[0] : undefined
};
}
},
_lookup: function (options) {
return [
options,
window
];
},
processors: {},
defaults: {}
}, {
setup: function (element, options) {
var cls = this.constructor, pluginname = cls.pluginName || cls._fullName, arr;
this.element = can.$(element);
if (pluginname && pluginname !== 'can_control') {
this.element.addClass(pluginname);
}
arr = can.data(this.element, 'controls');
if (!arr) {
arr = [];
can.data(this.element, 'controls', arr);
}
arr.push(this);
this.options = extend({}, cls.defaults, options);
this.on();
return [
this.element,
this.options
];
},
on: function (el, selector, eventName, func) {
if (!el) {
this.off();
var cls = this.constructor, bindings = this._bindings, actions = cls.actions, element = this.element, destroyCB = can.Control._shifter(this, 'destroy'), funcName, ready;
for (funcName in actions) {
if (actions.hasOwnProperty(funcName)) {
ready = actions[funcName] || cls._action(funcName, this.options, this);
if (ready) {
bindings.control[funcName] = ready.processor(ready.delegate || element, ready.parts[2], ready.parts[1], funcName, this);
}
}
}
can.bind.call(element, 'removed', destroyCB);
bindings.user.push(function (el) {
can.unbind.call(el, 'removed', destroyCB);
});
return bindings.user.length;
}
if (typeof el === 'string') {
func = eventName;
eventName = selector;
selector = el;
el = this.element;
}
if (func === undefined) {
func = eventName;
eventName = selector;
selector = null;
}
if (typeof func === 'string') {
func = can.Control._shifter(this, func);
}
this._bindings.user.push(binder(el, eventName, func, selector));
return this._bindings.user.length;
},
off: function () {
var el = this.element[0], bindings = this._bindings;
if (bindings) {
each(bindings.user || [], function (value) {
value(el);
});
each(bindings.control || {}, function (value) {
value(el);
});
}
this._bindings = {
user: [],
control: {}
};
},
destroy: function () {
if (this.element === null) {
can.dev.warn('can/control/control.js: Control already destroyed');
return;
}
var Class = this.constructor, pluginName = Class.pluginName || Class._fullName, controls;
this.off();
if (pluginName && pluginName !== 'can_control') {
this.element.removeClass(pluginName);
}
controls = can.data(this.element, 'controls');
controls.splice(can.inArray(this, controls), 1);
can.trigger(this, 'destroyed');
this.element = null;
}
});
var processors = can.Control.processors;
basicProcessor = function (el, event, selector, methodName, control) {
return binder(el, event, can.Control._shifter(control, methodName), selector);
};
each([
'change',
'click',
'contextmenu',
'dblclick',
'keydown',
'keyup',
'keypress',
'mousedown',
'mousemove',
'mouseout',
'mouseover',
'mouseup',
'reset',
'resize',
'scroll',
'select',
'submit',
'focusin',
'focusout',
'mouseenter',
'mouseleave',
'touchstart',
'touchmove',
'touchcancel',
'touchend',
'touchleave',
'inserted',
'removed',
'dragstart',
'dragenter',
'dragover',
'dragleave',
'drag',
'drop',
'dragend'
], function (v) {
processors[v] = basicProcessor;
});
return Control;
});