| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495 |
- var $ = require('../static'),
- utils = require('../utils'),
- isTag = utils.isTag,
- domEach = utils.domEach,
- hasOwn = Object.prototype.hasOwnProperty,
- camelCase = utils.camelCase,
- cssCase = utils.cssCase,
- rspace = /\s+/,
- dataAttrPrefix = 'data-',
- _ = {
- forEach: require('lodash.foreach'),
- extend: require('lodash.assignin'),
- some: require('lodash.some')
- },
- // Lookup table for coercing string data-* attributes to their corresponding
- // JavaScript primitives
- primitives = {
- null: null,
- true: true,
- false: false
- },
- // Attributes that are booleans
- rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,
- // Matches strings that look like JSON objects or arrays
- rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/;
- var getAttr = function(elem, name) {
- if (!elem || !isTag(elem)) return;
- if (!elem.attribs) {
- elem.attribs = {};
- }
- // Return the entire attribs object if no attribute specified
- if (!name) {
- return elem.attribs;
- }
- if (hasOwn.call(elem.attribs, name)) {
- // Get the (decoded) attribute
- return rboolean.test(name) ? name : elem.attribs[name];
- }
- // Mimic the DOM and return text content as value for `option's`
- if (elem.name === 'option' && name === 'value') {
- return $.text(elem.children);
- }
- // Mimic DOM with default value for radios/checkboxes
- if (elem.name === 'input' &&
- (elem.attribs.type === 'radio' || elem.attribs.type === 'checkbox') &&
- name === 'value') {
- return 'on';
- }
- };
- var setAttr = function(el, name, value) {
- if (value === null) {
- removeAttribute(el, name);
- } else {
- el.attribs[name] = value+'';
- }
- };
- exports.attr = function(name, value) {
- // Set the value (with attr map support)
- if (typeof name === 'object' || value !== undefined) {
- if (typeof value === 'function') {
- return domEach(this, function(i, el) {
- setAttr(el, name, value.call(el, i, el.attribs[name]));
- });
- }
- return domEach(this, function(i, el) {
- if (!isTag(el)) return;
- if (typeof name === 'object') {
- _.forEach(name, function(value, name) {
- setAttr(el, name, value);
- });
- } else {
- setAttr(el, name, value);
- }
- });
- }
- return getAttr(this[0], name);
- };
- var getProp = function (el, name) {
- if (!el || !isTag(el)) return;
-
- return el.hasOwnProperty(name)
- ? el[name]
- : rboolean.test(name)
- ? getAttr(el, name) !== undefined
- : getAttr(el, name);
- };
- var setProp = function (el, name, value) {
- el[name] = rboolean.test(name) ? !!value : value;
- };
- exports.prop = function (name, value) {
- var i = 0,
- property;
- if (typeof name === 'string' && value === undefined) {
- switch (name) {
- case 'style':
- property = this.css();
- _.forEach(property, function (v, p) {
- property[i++] = p;
- });
- property.length = i;
- break;
- case 'tagName':
- case 'nodeName':
- property = this[0].name.toUpperCase();
- break;
- default:
- property = getProp(this[0], name);
- }
- return property;
- }
- if (typeof name === 'object' || value !== undefined) {
- if (typeof value === 'function') {
- return domEach(this, function(i, el) {
- setProp(el, name, value.call(el, i, getProp(el, name)));
- });
- }
- return domEach(this, function(i, el) {
- if (!isTag(el)) return;
- if (typeof name === 'object') {
- _.forEach(name, function(val, name) {
- setProp(el, name, val);
- });
- } else {
- setProp(el, name, value);
- }
- });
- }
- };
- var setData = function(el, name, value) {
- if (!el.data) {
- el.data = {};
- }
- if (typeof name === 'object') return _.extend(el.data, name);
- if (typeof name === 'string' && value !== undefined) {
- el.data[name] = value;
- } else if (typeof name === 'object') {
- _.extend(el.data, name);
- }
- };
- // Read the specified attribute from the equivalent HTML5 `data-*` attribute,
- // and (if present) cache the value in the node's internal data store. If no
- // attribute name is specified, read *all* HTML5 `data-*` attributes in this
- // manner.
- var readData = function(el, name) {
- var readAll = arguments.length === 1;
- var domNames, domName, jsNames, jsName, value, idx, length;
- if (readAll) {
- domNames = Object.keys(el.attribs).filter(function(attrName) {
- return attrName.slice(0, dataAttrPrefix.length) === dataAttrPrefix;
- });
- jsNames = domNames.map(function(domName) {
- return camelCase(domName.slice(dataAttrPrefix.length));
- });
- } else {
- domNames = [dataAttrPrefix + cssCase(name)];
- jsNames = [name];
- }
- for (idx = 0, length = domNames.length; idx < length; ++idx) {
- domName = domNames[idx];
- jsName = jsNames[idx];
- if (hasOwn.call(el.attribs, domName)) {
- value = el.attribs[domName];
- if (hasOwn.call(primitives, value)) {
- value = primitives[value];
- } else if (value === String(Number(value))) {
- value = Number(value);
- } else if (rbrace.test(value)) {
- try {
- value = JSON.parse(value);
- } catch(e){ }
- }
- el.data[jsName] = value;
- }
- }
- return readAll ? el.data : value;
- };
- exports.data = function(name, value) {
- var elem = this[0];
- if (!elem || !isTag(elem)) return;
- if (!elem.data) {
- elem.data = {};
- }
- // Return the entire data object if no data specified
- if (!name) {
- return readData(elem);
- }
- // Set the value (with attr map support)
- if (typeof name === 'object' || value !== undefined) {
- domEach(this, function(i, el) {
- setData(el, name, value);
- });
- return this;
- } else if (hasOwn.call(elem.data, name)) {
- return elem.data[name];
- }
- return readData(elem, name);
- };
- /**
- * Get the value of an element
- */
- exports.val = function(value) {
- var querying = arguments.length === 0,
- element = this[0];
- if(!element) return;
- switch (element.name) {
- case 'textarea':
- return this.text(value);
- case 'input':
- switch (this.attr('type')) {
- case 'radio':
- if (querying) {
- return this.attr('value');
- } else {
- this.attr('value', value);
- return this;
- }
- break;
- default:
- return this.attr('value', value);
- }
- return;
- case 'select':
- var option = this.find('option:selected'),
- returnValue;
- if (option === undefined) return undefined;
- if (!querying) {
- if (!this.attr().hasOwnProperty('multiple') && typeof value == 'object') {
- return this;
- }
- if (typeof value != 'object') {
- value = [value];
- }
- this.find('option').removeAttr('selected');
- for (var i = 0; i < value.length; i++) {
- this.find('option[value="' + value[i] + '"]').attr('selected', '');
- }
- return this;
- }
- returnValue = option.attr('value');
- if (this.attr().hasOwnProperty('multiple')) {
- returnValue = [];
- domEach(option, function(i, el) {
- returnValue.push(getAttr(el, 'value'));
- });
- }
- return returnValue;
- case 'option':
- if (!querying) {
- this.attr('value', value);
- return this;
- }
- return this.attr('value');
- }
- };
- /**
- * Remove an attribute
- */
- var removeAttribute = function(elem, name) {
- if (!elem.attribs || !hasOwn.call(elem.attribs, name))
- return;
- delete elem.attribs[name];
- };
- exports.removeAttr = function(name) {
- domEach(this, function(i, elem) {
- removeAttribute(elem, name);
- });
- return this;
- };
- exports.hasClass = function(className) {
- return _.some(this, function(elem) {
- var attrs = elem.attribs,
- clazz = attrs && attrs['class'],
- idx = -1,
- end;
- if (clazz) {
- while ((idx = clazz.indexOf(className, idx+1)) > -1) {
- end = idx + className.length;
- if ((idx === 0 || rspace.test(clazz[idx-1]))
- && (end === clazz.length || rspace.test(clazz[end]))) {
- return true;
- }
- }
- }
- });
- };
- exports.addClass = function(value) {
- // Support functions
- if (typeof value === 'function') {
- return domEach(this, function(i, el) {
- var className = el.attribs['class'] || '';
- exports.addClass.call([el], value.call(el, i, className));
- });
- }
- // Return if no value or not a string or function
- if (!value || typeof value !== 'string') return this;
- var classNames = value.split(rspace),
- numElements = this.length;
- for (var i = 0; i < numElements; i++) {
- // If selected element isn't a tag, move on
- if (!isTag(this[i])) continue;
- // If we don't already have classes
- var className = getAttr(this[i], 'class'),
- numClasses,
- setClass;
- if (!className) {
- setAttr(this[i], 'class', classNames.join(' ').trim());
- } else {
- setClass = ' ' + className + ' ';
- numClasses = classNames.length;
- // Check if class already exists
- for (var j = 0; j < numClasses; j++) {
- var appendClass = classNames[j] + ' ';
- if (setClass.indexOf(' ' + appendClass) < 0)
- setClass += appendClass;
- }
- setAttr(this[i], 'class', setClass.trim());
- }
- }
- return this;
- };
- var splitClass = function(className) {
- return className ? className.trim().split(rspace) : [];
- };
- exports.removeClass = function(value) {
- var classes,
- numClasses,
- removeAll;
- // Handle if value is a function
- if (typeof value === 'function') {
- return domEach(this, function(i, el) {
- exports.removeClass.call(
- [el], value.call(el, i, el.attribs['class'] || '')
- );
- });
- }
- classes = splitClass(value);
- numClasses = classes.length;
- removeAll = arguments.length === 0;
- return domEach(this, function(i, el) {
- if (!isTag(el)) return;
- if (removeAll) {
- // Short circuit the remove all case as this is the nice one
- el.attribs.class = '';
- } else {
- var elClasses = splitClass(el.attribs.class),
- index,
- changed;
- for (var j = 0; j < numClasses; j++) {
- index = elClasses.indexOf(classes[j]);
- if (index >= 0) {
- elClasses.splice(index, 1);
- changed = true;
- // We have to do another pass to ensure that there are not duplicate
- // classes listed
- j--;
- }
- }
- if (changed) {
- el.attribs.class = elClasses.join(' ');
- }
- }
- });
- };
- exports.toggleClass = function(value, stateVal) {
- // Support functions
- if (typeof value === 'function') {
- return domEach(this, function(i, el) {
- exports.toggleClass.call(
- [el],
- value.call(el, i, el.attribs['class'] || '', stateVal),
- stateVal
- );
- });
- }
- // Return if no value or not a string or function
- if (!value || typeof value !== 'string') return this;
- var classNames = value.split(rspace),
- numClasses = classNames.length,
- state = typeof stateVal === 'boolean' ? stateVal ? 1 : -1 : 0,
- numElements = this.length,
- elementClasses,
- index;
- for (var i = 0; i < numElements; i++) {
- // If selected element isn't a tag, move on
- if (!isTag(this[i])) continue;
- elementClasses = splitClass(this[i].attribs.class);
- // Check if class already exists
- for (var j = 0; j < numClasses; j++) {
- // Check if the class name is currently defined
- index = elementClasses.indexOf(classNames[j]);
- // Add if stateValue === true or we are toggling and there is no value
- if (state >= 0 && index < 0) {
- elementClasses.push(classNames[j]);
- } else if (state <= 0 && index >= 0) {
- // Otherwise remove but only if the item exists
- elementClasses.splice(index, 1);
- }
- }
- this[i].attribs.class = elementClasses.join(' ');
- }
- return this;
- };
- exports.is = function (selector) {
- if (selector) {
- return this.filter(selector).length > 0;
- }
- return false;
- };
|