diff --git a/src/bonzo.js b/src/bonzo.js index 9c9ae9f..0bafb44 100644 --- a/src/bonzo.js +++ b/src/bonzo.js @@ -86,7 +86,6 @@ return node } - /** * @param {string} c a class name to test * @return {boolean} @@ -553,6 +552,17 @@ return this.remove() } + /** + * @param {Object=} opt_host an optional host scope (primarily used when integrated with Ender) + * @return {Bonzo} + */ + , clone: function (opt_host) { + var ret = [] // don't change original array + , l, i + for (i = 0, l = this.length; i < l; i++) ret[i] = cloneNode(opt_host || this, this[i]) + return bonzo(ret) + } + // class management /** @@ -705,7 +715,7 @@ * @return {Element|Node} */ , related: function (method) { - return this.map( + return bonzo(this.map( function (el) { el = el[method] while (el && el.nodeType !== 1) { @@ -716,7 +726,7 @@ function (el) { return el } - ) + )) } diff --git a/src/ender.js b/src/ender.js index 5932fe9..8492d4d 100644 --- a/src/ender.js +++ b/src/ender.js @@ -74,6 +74,10 @@ return $(b(this).previous()) } + , related: function (t) { + return $(b(this).related(t)) + } + , appendTo: function (t) { return b(this.selector).appendTo(t, this) } @@ -90,6 +94,10 @@ return b(this.selector).insertBefore(t, this) } + , clone: function () { + return $(b(this).clone(this)) + } + , siblings: function () { var i, l, p, r = [] for (i = 0, l = this.length; i < l; i++) { diff --git a/tests/dommanip-test.js b/tests/dommanip-test.js index 7bca13e..5bb8fe0 100644 --- a/tests/dommanip-test.js +++ b/tests/dommanip-test.js @@ -214,4 +214,29 @@ sink('DOM Manipulation', function(test, ok, before, after, assert) { ok($(document.createElement('div')).detach(), 'can call detach on already detached elements') } catch (e) { ok(false, 'error detaching detached element: ' + e); console && console.log(e,e.stack) } }) + + test('should clone() detached node list', 8, function () { + var orig = $.create('
spantastic
') + , clone = $(orig).clone() + + ok(orig.length == 2, 'sanity check, original fixture has 2 parent elements') + ok(orig.length == clone.length, 'clone has 2 parent elements') + ok(orig[0] !== clone[0], 'clone element !== original element') + ok(orig[1] !== clone[1], 'clone element !== original element') + ok(orig[0].childNodes[0] !== clone[0].childNodes[0], 'clone element !== original element (child node)') + ok(orig[1].childNodes[0] !== clone[1].childNodes[0], 'clone element !== original element (child node)') + ok(clone[0].childNodes[0].href == 'http://foo/', 'cloned attributes') + ok(clone[1].id == 'paragraphtastic', 'cloned attributes') + }) + + test('should clone() attached node list', 2, function () { + var html = Q('#clonesrc')[0].innerHTML.toLowerCase().replace(/\s/g, '') + var src = $(Q('#clonesrc > *')) + src.clone().appendTo('#clonedst') + var newhtml = Q('#clonedst')[0].innerHTML.toLowerCase().replace(/\s/g, '') + ok(html == newhtml, 'cloning attached node duplicates HTML') + src.clone().appendTo('#clonedst') // do it again! + newhtml = Q('#clonedst')[0].innerHTML.toLowerCase().replace(/\s/g, '') + ok(html + html == newhtml, 'cloning attached node duplicates HTML (again!)') + }) }) \ No newline at end of file diff --git a/tests/dommanip_insertions-test.js b/tests/dommanip_insertions-test.js index 90e024d..7967146 100644 --- a/tests/dommanip_insertions-test.js +++ b/tests/dommanip_insertions-test.js @@ -2271,6 +2271,7 @@ sink('DOM Manipulation - insertions', function(test, ok, before, after, assert) */ test('nodes with text nodes being appended with $.create', function (complete) { var tree = $.create('hey there') + console.log(tree) ok(tree.length == 2, 'created two nodes') ok(tree[0] && tree[0].nodeType == 1, 'first node is an element') ok(tree[1] && tree[1].nodeType == 3, 'second node is a text node') diff --git a/tests/emptycollection-test.js b/tests/emptycollection-test.js index cf911f3..36379c2 100644 --- a/tests/emptycollection-test.js +++ b/tests/emptycollection-test.js @@ -44,9 +44,9 @@ sink('Empty-collection safety', function (test, ok) { // FUNCTION NAME ARGUMENT SIGNATURE ARGUMENTS ARRAY EXPECTED RESULT MATCHER first: { str: '', args: [], expect: isEmptyContainer } , last: { str: '', args: [], expect: isEmptyContainer } - , parent: { str: '', args: [], expect: isEmptyArray } - , next: { str: '', args: [], expect: isEmptyArray } - , previous: { str: '', args: [], expect: isEmptyArray } + , parent: { str: '', args: [], expect: isEmptyContainer } + , next: { str: '', args: [], expect: isEmptyContainer } + , previous: { str: '', args: [], expect: isEmptyContainer } , dim: { str: '', args: [], expect: function(r) { return r.height === 0 && r.width === 0 } } , get: { str: 'index', args: [0], expect: isNull } , detach: { str: '', args: [], expect: isSameContainer } @@ -74,12 +74,13 @@ sink('Empty-collection safety', function (test, ok) { , prepend: { str: 'html', args: [''], expect: isSameContainer } , appendTo: { str: 'html', args: [''], expect: isSameContainer } , prependTo: { str: 'html', args: [''], expect: isSameContainer } - , related: { str: 'method', args: ['parentNode'], expect: isEmptyArray } + , related: { str: 'method', args: ['parentNode'], expect: isEmptyContainer } , before: { str: 'html', args: [''], expect: isSameContainer } , after: { str: 'html', args: [''], expect: isSameContainer } , insertBefore: { str: 'html', args: [''], expect: isSameContainer } , insertAfter: { str: 'html', args: [''], expect: isSameContainer } , replaceWith: { str: 'html', args: [''], expect: isEmptyContainer } + , clone: { str: '', args: [], expect: isEmptyContainer } , css: [ { str: 'prop', args: ['color'], expect: isNull } // not sure about this one, depending on the browser you might get "" for an empty property on a real element and undefined for an unknown property on a real element , { str: 'prop, val', args: ['color', 'red'], expect: isSameContainer } , { str: '{prop: val}', args: [{color: 'red'}], expect: isSameContainer } diff --git a/tests/ender-js.js b/tests/ender-js.js index d95ce22..7d43a61 100644 --- a/tests/ender-js.js +++ b/tests/ender-js.js @@ -1,10 +1,10 @@ /*! * Ender: open module JavaScript framework (client-lib) - * copyright Dustin Diaz & Jacob Thornton 2011 (@ded @fat) - * http://ender.no.de + * copyright Dustin Diaz & Jacob Thornton 2011-2012 (@ded @fat) + * http://ender.jit.su * License MIT */ -!function (context) { +(function (context) { // a global object for node.js module compatiblity // ============================================ @@ -16,17 +16,20 @@ // ============================================ var modules = {} - , old = context.$ + , old = context['$'] + , oldEnder = context['ender'] + , oldRequire = context['require'] + , oldProvide = context['provide'] function require (identifier) { // modules can be required from ender's build system, or found on the window - var module = modules[identifier] || window[identifier] - if (!module) throw new Error("Requested module '" + identifier + "' has not been defined.") + var module = modules['$' + identifier] || window[identifier] + if (!module) throw new Error("Ender Error: Requested module '" + identifier + "' has not been defined.") return module } function provide (name, what) { - return (modules[name] = what) + return (modules['$' + name] = what) } context['provide'] = provide @@ -37,48 +40,80 @@ return o } - function boosh(s, r, els) { + /** + * main Ender return object + * @constructor + * @param {Array|Node|string} s a CSS selector or DOM node(s) + * @param {Array.|Node} r a root node(s) + */ + function Ender(s, r) { + var elements + , i + + this.selector = s // string || node || nodelist || window - if (typeof s == 'string' || s.nodeName || (s.length && 'item' in s) || s == window) { - els = ender._select(s, r) - els.selector = s - } else els = isFinite(s.length) ? s : [s] - return aug(els, boosh) + if (typeof s == 'undefined') { + elements = [] + this.selector = '' + } else if (typeof s == 'string' || s.nodeName || (s.length && 'item' in s) || s == window) { + elements = ender._select(s, r) + } else { + elements = isFinite(s.length) ? s : [s] + } + this.length = elements.length + for (i = this.length; i--;) this[i] = elements[i] + } + + /** + * @param {function(el, i, inst)} fn + * @param {Object} opt_scope + * @returns {Ender} + */ + Ender.prototype['forEach'] = function (fn, opt_scope) { + var i, l + // opt out of native forEach so we can intentionally call our own scope + // defaulting to the current item and be able to return self + for (i = 0, l = this.length; i < l; ++i) i in this && fn.call(opt_scope || this[i], this[i], i, this) + // return self for chaining + return this } + Ender.prototype.$ = ender // handy reference to self + + function ender(s, r) { - return boosh(s, r) + return new Ender(s, r) } - aug(ender, { - _VERSION: '0.3.6' - , fn: boosh // for easy compat to jQuery plugins - , ender: function (o, chain) { - aug(chain ? boosh : ender, o) - } - , _select: function (s, r) { - return (r || document).querySelectorAll(s) - } - }) - - aug(boosh, { - forEach: function (fn, scope, i) { - // opt out of native forEach so we can intentionally call our own scope - // defaulting to the current item and be able to return self - for (i = 0, l = this.length; i < l; ++i) i in this && fn.call(scope || this[i], this[i], i, this) - // return self for chaining - return this - }, - $: ender // handy reference to self - }) - - ender.noConflict = function () { - context.$ = old + ender['_VERSION'] = '0.4.3-dev' + + ender.fn = Ender.prototype // for easy compat to jQuery plugins + + ender.ender = function (o, chain) { + aug(chain ? Ender.prototype : ender, o) + } + + ender._select = function (s, r) { + if (typeof s == 'string') return (r || document).querySelectorAll(s) + if (s.nodeName) return [s] + return s + } + + + // use callback to receive Ender's require & provide and remove them from global + ender.noConflict = function (callback) { + context['$'] = old + if (callback) { + context['provide'] = oldProvide + context['require'] = oldRequire + context['ender'] = oldEnder + if (typeof callback == 'function') callback(require, provide, this) + } return this } if (typeof module !== 'undefined' && module.exports) module.exports = ender // use subscript notation as extern for Closure compilation - context['ender'] = context['$'] = context['ender'] || ender + context['ender'] = context['$'] = ender -}(this); \ No newline at end of file +}(this)); \ No newline at end of file diff --git a/tests/tests.html b/tests/tests.html index 6c2ffd2..6004dff 100644 --- a/tests/tests.html +++ b/tests/tests.html @@ -120,6 +120,8 @@ +TEXT
foo