diff options
Diffstat (limited to 'src/jqLite.js')
| -rw-r--r-- | src/jqLite.js | 461 | 
1 files changed, 293 insertions, 168 deletions
| diff --git a/src/jqLite.js b/src/jqLite.js index 01a563b6..40994161 100644 --- a/src/jqLite.js +++ b/src/jqLite.js @@ -14,20 +14,6 @@ var jqCache = {},  function jqNextId() { return (jqId++); } -function jqClearData(element) { -  var cacheId = element[jqName], -      cache = jqCache[cacheId]; -  if (cache) { -    forEach(cache.bind || {}, function(fn, type){ -      removeEventListenerFn(element, type, fn); -    }); -    delete jqCache[cacheId]; -    if (msie) -      element[jqName] = ''; // ie does not allow deletion of attributes on elements. -    else -      delete element[jqName]; -  } -}  function getStyle(element) {    var current = {}, style = element[0].style, value, name, i; @@ -46,56 +32,120 @@ function getStyle(element) {    return current;  } -function JQLite(element) { -  if (!isElement(element) && isDefined(element.length) && element.item && !isWindow(element)) { -    for(var i=0; i < element.length; i++) { -      this[i] = element[i]; +if (msie) { +  extend(JQLite.prototype, { +    text: function(value) { +      var e = this[0]; +      // NodeType == 3 is text node +      if (e.nodeType == 3) { +        if (isDefined(value)) e.nodeValue = value; +        return e.nodeValue; +      } else { +        if (isDefined(value)) e.innerText = value; +        return e.innerText; +      }      } -    this.length = element.length; +  }); +} + +///////////////////////////////////////////// +function jqLiteWrap(element) { +  if (isString(element) && element.charAt(0) != '<') { +    throw new Error('selectors not implemented'); +  } +  return new JQLite(element); +} + +function JQLite(element) { +  if (element instanceof JQLite) { +    return element; +  } else if (isString(element)) { +    var div = document.createElement('div'); +    // Read about the NoScope elements here: +    // http://msdn.microsoft.com/en-us/library/ms533897(VS.85).aspx +    div.innerHTML = '<div> </div>' + element; // IE insanity to make NoScope elements work! +    div.removeChild(div.firstChild); // remove the superfluous div +    JQLiteAddNodes(this, div.childNodes); +    this.remove(); // detach the elements form the temporary DOM div.    } else { -    this[0] = element; -    this.length = 1; +    JQLiteAddNodes(this, element);    }  } -JQLite.prototype = { -  data: function(key, value) { -    var element = this[0], -        cacheId = element[jqName], -        cache = jqCache[cacheId || -1]; -    if (isDefined(value)) { -      if (!cache) { -        element[jqName] = cacheId = jqNextId(); -        cache = jqCache[cacheId] = {}; -      } -      cache[key] = value; -    } else { -      return cache ? cache[key] : _null; -    } -  }, +function JQLiteCloneNode(element) { +  return element.cloneNode(true); +} -  removeData: function(){ -    jqClearData(this[0]); -  }, +function JQLiteDealoc(element){ +  JQLiteRemoveData(element); +  for ( var i = 0, children = element.childNodes || []; i < children.length; i++) { +    JQLiteDealoc(children[i]); +  } +} -  dealoc: function(){ -    (function dealoc(element){ -      jqClearData(element); -      for ( var i = 0, children = element.childNodes || []; i < children.length; i++) { -        dealoc(children[i]); -      } -    })(this[0]); -  }, +function JQLiteRemoveData(element) { +  var cacheId = element[jqName], +  cache = jqCache[cacheId]; +  if (cache) { +    forEach(cache.bind || {}, function(fn, type){ +      removeEventListenerFn(element, type, fn); +    }); +    delete jqCache[cacheId]; +    element[jqName] = undefined; // ie does not allow deletion of attributes on elements. +  } +} -  scope: function() { -    var scope, element = this; -    while (element && element.length && !(scope = element.data($$scope))) { -      element = element.parent(); +function JQLiteData(element, key, value) { +  var cacheId = element[jqName], +      cache = jqCache[cacheId || -1]; +  if (isDefined(value)) { +    if (!cache) { +      element[jqName] = cacheId = jqNextId(); +      cache = jqCache[cacheId] = {};      } -    return scope; -  }, +    cache[key] = value; +  } else { +    return cache ? cache[key] : _null; +  } +} + +function JQLiteHasClass(element, selector, _) { +  // the argument '_' is important, since it makes the function have 3 arguments, which +  // is neede for delegate function to realize the this is a getter. +  var className = " " + selector + " "; +  return ((" " + element.className + " ").replace(/[\n\t]/g, " ").indexOf( className ) > -1); +} + +function JQLiteRemoveClass(element, selector) { +  element.className = trim( +      (" " + element.className + " ") +      .replace(/[\n\t]/g, " ") +      .replace(" " + selector + " ", "") +  ); +} +function JQLiteAddClass(element, selector ) { +  if (!JQLiteHasClass(element, selector)) { +    element.className = trim(element.className + ' ' + selector); +  } +} + +function JQLiteAddNodes(root, elements) { +  if (elements) { +    elements = (!elements.nodeName && isDefined(elements.length)) +      ? elements +      : [ elements ]; +    for(var i=0; i < elements.length; i++) { +      if (elements[i].nodeType != 11) +        root.push(elements[i]); +    } +  } +} +////////////////////////////////////////// +// Functions which are declared directly. +////////////////////////////////////////// +var JQLitePrototype = JQLite.prototype = extend([], {    ready: function(fn) {      var fired = false; @@ -107,15 +157,137 @@ JQLite.prototype = {      this.bind('DOMContentLoaded', trigger); // works for modern browsers and IE9      // we can not use jqLite since we are not done loading and jQuery could be loaded later. -    new JQLite(window).bind('load', trigger); // fallback to window.onload for others +    jqLiteWrap(window).bind('load', trigger); // fallback to window.onload for others +  }, +  toString: function(){ +    var value = []; +    forEach(this, function(e){ value.push('' + e);}); +    return '[' + value.join(', ') + ']'; +  } +}); + +////////////////////////////////////////// +// Functions iterating getter/setters. +// these functions return self on setter and +// value on get. +////////////////////////////////////////// +forEach({ +  data: JQLiteData, + +  scope: function(element) { +    var scope; +    while (element && !(scope = jqLite(element).data($$scope))) { +      element = element.parentNode; +    } +    return scope;    }, -  bind: function(type, fn){ -    var self = this, -        element = self[0], -        bind = self.data('bind'), +  removeAttr: function(element,name) { +    element.removeAttribute(name); +  }, + +  hasClass: JQLiteHasClass, + +  css: function(element, name, value) { +    if (isDefined(value)) { +      element.style[name] = value; +    } else { +      return element.style[name]; +    } +  }, + +  attr: function(element, name, value){ +    if (isDefined(value)) { +      element.setAttribute(name, value); +    } else if (element.getAttribute) { +      // the extra argument "2" is to get the right thing for a.href in IE, see jQuery code +      // some elements (e.g. Document) don't have get attribute, so return undefined +      return element.getAttribute(name, 2); +    } +  }, + +  text: extend(msie +      ? function(element, value) { +        // NodeType == 3 is text node +        if (element.nodeType == 3) { +          if (isUndefined(value)) +            return element.nodeValue; +          element.nodeValue = value; +        } else { +          if (isUndefined(value)) +            return element.innerText; +          element.innerText = value; +        } +      } +      : function(element, value) { +        if (isUndefined(value)) { +          return element.textContent; +        } +        element.textContent = value; +      }, {$dv:''}), + +  val: function(element, value) { +    if (isUndefined(value)) { +      return element.value; +    } +    element.value = value; +  }, + +  html: function(element, value) { +    if (isUndefined(value)) { +      return element.innerHTML; +    } +    for (var i = 0, childNodes = element.childNodes; i < childNodes.length; i++) { +      JQLiteDealoc(childNodes[i]); +    } +    element.innerHTML = value; +  } +}, function(fn, name){ +  /** +   * Properties: writes return selection, reads return first value +   */ +  JQLite.prototype[name] = function(arg1, arg2) { +    if ((fn.length == 2 ? arg1 : arg2) === undefined) { +      if (isObject(arg1)) { +        // we are a write, but the object properties are the key/values +        for(var i=0; i < this.length; i++) { +          for ( var key in arg1) { +            fn(this[i], key, arg1[key]); +          } +        } +        // return self for chaining +        return this; +      } else { +        // we are a read, so read the first child. +        if (this.length) +          return fn(this[0], arg1, arg2); +      } +    } else { +      // we are a write, so apply to all children +      for(var i=0; i < this.length; i++) { +        fn(this[i], arg1, arg2); +      } +      // return self for chaining +      return this; +    } +    return fn.$dv; +  }; +}); + +////////////////////////////////////////// +// Functions iterating traversal. +// These functions chain results into a single +// selector. +////////////////////////////////////////// +forEach({ +  removeData: JQLiteRemoveData, + +  dealoc: JQLiteDealoc, + +  bind: function(element, type, fn){ +    var bind = JQLiteData(element, 'bind'),          eventHandler; -    if (!bind) this.data('bind', bind = {}); +    if (!bind) JQLiteData(element, 'bind', bind = {});      forEach(type.split(' '), function(type){        eventHandler = bind[type];        if (!eventHandler) { @@ -131,7 +303,7 @@ JQLite.prototype = {              };            }            forEach(eventHandler.fns, function(fn){ -            fn.call(self, event); +            fn.call(element, event);            });          };          eventHandler.fns = []; @@ -141,133 +313,86 @@ JQLite.prototype = {      });    }, -  replaceWith: function(replaceNode) { -    this[0].parentNode.replaceChild(jqLite(replaceNode)[0], this[0]); -  }, - -  children: function() { -    return new JQLite(this[0].childNodes); -  }, - -  append: function(node) { -    var self = this[0]; -    node = jqLite(node); -    forEach(node, function(child){ -      self.appendChild(child); +  replaceWith: function(element, replaceNode) { +    var index, parent = element.parentNode; +    JQLiteDealoc(element); +    forEach(new JQLite(replaceNode), function(node){ +      if (index) { +        parent.insertBefore(node, index.nextSibling); +      } else { +        parent.replaceChild(node, element); +      } +      index = node;      });    }, -  remove: function() { -    this.dealoc(); -    var parentNode = this[0].parentNode; -    if (parentNode) parentNode.removeChild(this[0]); -  }, - -  removeAttr: function(name) { -    this[0].removeAttribute(name); -  }, - -  after: function(element) { -    this[0].parentNode.insertBefore(jqLite(element)[0], this[0].nextSibling); -  }, - -  hasClass: function(selector) { -    var className = " " + selector + " "; -    if ( (" " + this[0].className + " ").replace(/[\n\t]/g, " ").indexOf( className ) > -1 ) { -      return true; -    } -    return false; -  }, - -  removeClass: function(selector) { -    this[0].className = trim((" " + this[0].className + " ").replace(/[\n\t]/g, " ").replace(" " + selector + " ", "")); +  children: function(element) { +    var children = []; +    forEach(element.childNodes, function(element){ +      if (element.nodeName != '#text') +        children.push(element); +    }); +    return children;    }, -  toggleClass: function(selector, condition) { -   var self = this; -   (condition ? self.addClass : self.removeClass).call(self, selector); +  append: function(element, node) { +    forEach(new JQLite(node), function(child){ +      element.appendChild(child); +    });    }, -  addClass: function( selector ) { -    if (!this.hasClass(selector)) { -      this[0].className = trim(this[0].className + ' ' + selector); -    } +  remove: function(element) { +    JQLiteDealoc(element); +    var parent = element.parentNode; +    if (parent) parent.removeChild(element);    }, -  css: function(name, value) { -    var style = this[0].style; -    if (isString(name)) { -      if (isDefined(value)) { -        style[name] = value; -      } else { -        return style[name]; -      } -    } else { -      extend(style, name); -    } +  after: function(element, newElement) { +    var index = element, parent = element.parentNode; +    forEach(new JQLite(newElement), function(node){ +      parent.insertBefore(node, index.nextSibling); +      index = node; +    });    }, -  attr: function(name, value){ -    var e = this[0]; -    if (isObject(name)) { -      forEach(name, function(value, name){ -        e.setAttribute(name, value); -      }); -    } else if (isDefined(value)) { -      e.setAttribute(name, value); -    } else { -      // the extra argument "2" is to get the right thing for a.href in IE, see jQuery code -      // some elements (e.g. Document) don't have get attribute, so return undefined -      if (e.getAttribute) return e.getAttribute(name, 2); -    } -  }, +  addClass: JQLiteAddClass, +  removeClass: JQLiteRemoveClass, -  text: function(value) { -    if (isDefined(value)) { -      this[0].textContent = value; +  toggleClass: function(element, selector, condition) { +    if (isUndefined(condition)) { +      condition = !JQLiteHasClass(element, selector);      } -    return this[0].textContent; +    (condition ? JQLiteAddClass : JQLiteRemoveClass)(element, selector);    }, -  val: function(value) { -    if (isDefined(value)) { -      this[0].value = value; -    } -    return this[0].value; +  parent: function(element) { +    // in IE it returns undefined, but we need differentiate it from functions which have no return +    return element.parentNode || null;    }, -  html: function(value) { -    if (isDefined(value)) { -      var i = 0, childNodes = this[0].childNodes; -      for ( ; i < childNodes.length; i++) { -        jqLite(childNodes[i]).dealoc(); -      } -      this[0].innerHTML = value; -    } -    return this[0].innerHTML; +  find: function(element, selector) { +    return element.getElementsByTagName(selector);    }, -  parent: function() { -    return jqLite(this[0].parentNode); -  }, - -  clone: cloneNode, -  cloneNode: cloneNode -}; -function cloneNode() { return jqLite(this[0].cloneNode(true)); } - -if (msie) { -  extend(JQLite.prototype, { -    text: function(value) { -      var e = this[0]; -      // NodeType == 3 is text node -      if (e.nodeType == 3) { -        if (isDefined(value)) e.nodeValue = value; -        return e.nodeValue; +  clone: JQLiteCloneNode, +  cloneNode: JQLiteCloneNode +}, function(fn, name){ +  /** +   * chaining functions +   */ +  JQLite.prototype[name] = function(arg1, arg2) { +    var value; +    for(var i=0; i < this.length; i++) { +      if (value == undefined) { +        value = fn(this[i], arg1, arg2); +        if (value !== undefined) { +          // any function which returns a value needs to be wrapped +          value = jqLite(value); +        }        } else { -        if (isDefined(value)) e.innerText = value; -        return e.innerText; +        JQLiteAddNodes(value, fn(this[i], arg1, arg2));        }      } -  }); -} +    return value == undefined ? this : value; +  }; +}); | 
