Posted By

komposition on 02/29/08


Tagged

css js ie textmate pseudo-classes


Versions (?)

Who likes this?

1 person have marked this snippet as a favorite

adix


offspring.js


 / Published in: Other
 

  1. /*
  2. Offspring.js -- adds the following classes as needed:
  3.  
  4. .first-child
  5. .last-child
  6. .only-child
  7. .nth-child-odd
  8. .nth-child-even
  9. .nth-child-##
  10.  
  11. Configuration:
  12.  
  13. Offspring can be configured by defining an "offspringConfiguration"
  14. object before referencing offspring.js. That object can contain
  15. one or more of these parameters. (If any parameter is omitted -- which
  16. is fine -- it gets the default value as described below.)
  17.  
  18. offspringConfiguration =
  19. {
  20. runningMode: 'full', <-- valid values are 'full' and 'light' (default: 'full')
  21. autoStart: true, <-- valid values are true and false (default: true)
  22. shouldRemoveOldOffspringClassesFirst: false <-- valid values are true and false (default: false)
  23. }
  24.  
  25. * runningMode:
  26. 'full' -- Offspring applies all of its classes (as listed at the very top) [default]
  27. 'light' -- Offspring only applies 'first-child', 'last-child', and 'only-child',
  28. omitting 'nth-child-odd', 'nth-child-even', and 'nth-child-##'.
  29. (This may allow for faster page-processing in certain scenarios.)
  30.  
  31. * autoStart:
  32. true -- Offspring runs automatically as soon as the DOM is ready [default]
  33. false -- Offspring must be run manually. This can be done by calling Offspring.start();
  34.  
  35. * shouldRemoveOldOffspringClassesFirst:
  36. true --Offspring first removes any old Offspring classes before applying the new ones.
  37. (This might be of use if Offspring is to be called on a page that has already
  38. been processed, such as if a table has been sorted or content has been loaded
  39. via Ajax.)
  40. false -- Offspring applies its classes without first removing old Offspring classes that
  41. might be there. Unless you're doing fancy DOM updates, this is probably the
  42. better option in most cases. [default]
  43.  
  44. ================================================================== */
  45.  
  46.  
  47. var offspring = {
  48. firstChildClass: "first-child",
  49. lastChildClass: "last-child",
  50. oddChildClass: "nth-child-odd",
  51. evenChildClass: "nth-child-even",
  52. onlyChildClass: "only-child",
  53. nthChildClassPrefix: "nth-child-",
  54.  
  55. classNamesArray: [],
  56. classNameSubstringsArray: [],
  57.  
  58. cacheLevel: 0, // current size of the classNames cache
  59.  
  60. nthChildren: [],
  61.  
  62. regularHashTable: [],
  63. regularHashTableArray: [],
  64.  
  65. lastChildHashTable: [],
  66. lastChildHashTableArray: [],
  67.  
  68. /* Configuration defaults */
  69. configuration:
  70. {
  71. runningMode: 'full', /* Possible values: 'full' / 'light' */
  72. autoStart: true, /* If Offspring is configured in autoStart mode (which it is by default),
  73. it runs as soon as the DOM is ready */
  74. shouldRemoveOldOffspringClassesFirst: false /* If this is set to 'true', Offspring first
  75. removes any old Offspring-related classes
  76. before applying the new ones */
  77. },
  78.  
  79. // Initialize
  80. init: function() {
  81.  
  82. /*
  83. Offspring's configuration is stored in Offspring.configuration, but
  84. that con be overridden by users by defining an "offspringConfiguratin"
  85. object.
  86. */
  87. if (typeof offspringConfiguration != "undefined")
  88. {
  89. for (var configParameter in offspringConfiguration)
  90. {
  91. this.configuration[configParameter] = offspringConfiguration[configParameter];
  92. }
  93.  
  94. // Make sure this option is stored in lowercase
  95. this.configuration.runningMode = this.configuration.runningMode.toLowerCase();
  96. }
  97.  
  98.  
  99. /* Set the values for classNamesArray & classNameSubstringArray */
  100.  
  101. switch (this.configuration.runningMode)
  102. {
  103. case 'full':
  104. // this represents all possible offspring-related classnames
  105. this.classNamesArray = [this.firstChildClass, this.lastChildClass, this.oddChildClass, this.evenChildClass, this.onlyChildClass];
  106.  
  107. // this represents a list of substrings to match such as for removing classNames
  108. this.classNameSubstringsArray = [this.nthChildClassPrefix];
  109. break;
  110.  
  111. case 'light':
  112. // this represents all possible offspring-related classnames
  113. this.classNamesArray = [this.firstChildClass, this.lastChildClass, this.onlyChildClass];
  114.  
  115. // this represents a list of substrings to match such as for removing classNames
  116. this.classNameSubstringsArray = [];
  117. break;
  118. }
  119.  
  120. // Define the iterator function on-the-fly depending
  121. // on the configuration options that were sent in
  122. this.defineTraverseChildrenFunction();
  123.  
  124. // Define the fillCacheTo funtion's iterator on-the-fly
  125. // depending on the configuration options that were sent in
  126. this.defineFillCacheToFunction();
  127. this.fillCacheTo(); // seed the cache with a basic set of values
  128.  
  129.  
  130. /* If Offspring is configured in autoStart mode (which it is by default),
  131. it runs as soon as the DOM is ready */
  132. if (this.configuration.autoStart)
  133. {
  134. var _this = this; // Closure
  135.  
  136. this.ContentLoaded(window, function() {
  137. _this.start();
  138. });
  139. }
  140.  
  141. },
  142.  
  143. // Executed once the page has loaded
  144. start: function() {
  145. var startTime = new Date();
  146.  
  147. this.traverseChildren(document.getElementsByTagName("body")[0]);
  148.  
  149. var endTime = new Date();
  150. // alert("Offspring Exec time: " + (endTime.getTime() - startTime.getTime()) + "ms");
  151. // window.status += "Offspring Exec time: " + (endTime.getTime() - startTime.getTime()) + "ms";
  152.  
  153. },
  154.  
  155. /* Maintenance note for defineTraverseChildrenFunction:
  156.  
  157. There are several blocks of code that are marked off as "traverseChildren.A"
  158. or "traverseChildren.B" -- each of these are identical, respectively. (That is,
  159. all "traverseChildren.A" blocks are the same and all "traverseChildren.B" are
  160. the same.)
  161.  
  162. So, why not just create a function where the code can be kept in one place?
  163. While normally a sensible idea, I decided against that approach only so
  164. that the speed hits associated with the creation of the function stack
  165. could be averted. At the same time, I didn't want to compromise
  166. the code's maintainability; so, if any block needs to be updated, they
  167. can all be kept in sync with some basic copy-n-pasting from one
  168. block to the next.
  169. */
  170.  
  171.  
  172. /* This defines the internal iterator function on-the-fly,
  173. depending on the configuration options */
  174. defineTraverseChildrenFunction: function() {
  175.  
  176. switch (this.configuration.shouldRemoveOldOffspringClassesFirst)
  177. {
  178. case true: // shouldRemoveOldOffspringClassesFirst is true
  179.  
  180. switch (this.configuration.runningMode)
  181. {
  182. case 'full': // 'full' running mode and shouldRemoveOldOffspringClassesFirst is true
  183. this.traverseChildren = function(parent)
  184. {
  185. /* ============= Begin Code Block "traverseChildren.A" ================ */
  186.  
  187. // If the node has no children, exit
  188. if (!parent.childNodes.length) return;
  189.  
  190.  
  191. /* First, gather up all the element nodes */
  192. var childElementNodes = [];
  193.  
  194. var testNode = parent.childNodes[0]; // initialize
  195.  
  196. while (testNode)
  197. {
  198. if (testNode.nodeType == 1)
  199. {
  200. childElementNodes.push(testNode);
  201. }
  202. testNode = testNode.nextSibling;
  203. }
  204.  
  205. /*
  206. empty this variable to ensure that the JavaScript
  207. interpreter doesn't have to update the variable's
  208. nodelist as DOM changes are made
  209. */
  210. testNode = null;
  211.  
  212. var childElementNodesLength = childElementNodes.length;
  213.  
  214. // If no element nodes were found, exit
  215. if (!childElementNodesLength) return;
  216.  
  217. // Make sure that the CSS-classnames cache has enough entries to cover
  218. // the number of child nodes
  219. if (childElementNodesLength > this.cacheLevel)
  220. {
  221. this.fillCacheTo(childElementNodesLength);
  222. }
  223.  
  224. var lastIndex = childElementNodesLength - 1; // index of the last element node
  225.  
  226. /* ============= /End Code Block "traverseChildren.A" ================ */
  227.  
  228. // First, take care of all but the last element
  229. for (var i = 0; i < lastIndex; i++)
  230. {
  231. var currentElement = childElementNodes[i];
  232.  
  233. this.removeMultipleClassNames(currentElement, this.classNamesArray, this.classNameSubstringsArray);
  234.  
  235. // argument syntax: node to act upon, current index, boolean for whether isLast
  236. this._addOffspringClassNames(currentElement, i, false);
  237. this.traverseChildren(currentElement);
  238. }
  239.  
  240. currentElement = null; // prevent memory leaks
  241.  
  242. // Then, take care of the last one
  243. var lastElement = childElementNodes[lastIndex];
  244.  
  245. this.removeMultipleClassNames(lastElement, this.classNamesArray, this.classNameSubstringsArray);
  246.  
  247. this._addOffspringClassNames(lastElement, lastIndex, true);
  248. this.traverseChildren(lastElement);
  249.  
  250. lastElement = null; // prevent memory leaks
  251.  
  252. /* ============= Begin Code Block "traverseChildren.B" ================ */
  253.  
  254. // prevent memory leaks
  255. lastElement = null;
  256. parent = null;
  257.  
  258. /* ============= /End Code Block "traverseChildren.B" ================ */
  259.  
  260. }; // end of traverseChildren function definition
  261. break;
  262.  
  263. case 'light': // 'light' running mode and shouldRemoveOldOffspringClassesFirst is true
  264. this.traverseChildren = function(parent)
  265. {
  266. /* ============= Begin Code Block "traverseChildren.A" ================ */
  267.  
  268. // If the node has no children, exit
  269. if (!parent.childNodes.length) return;
  270.  
  271.  
  272. /* First, gather up all the element nodes */
  273. var childElementNodes = [];
  274.  
  275. var testNode = parent.childNodes[0]; // initialize
  276.  
  277. while (testNode)
  278. {
  279. if (testNode.nodeType == 1)
  280. {
  281. childElementNodes.push(testNode);
  282. }
  283. testNode = testNode.nextSibling;
  284. }
  285.  
  286. /*
  287. empty this variable to ensure that the JavaScript
  288. interpreter doesn't have to update the variable's
  289. nodelist as DOM changes are made
  290. */
  291. testNode = null;
  292.  
  293. var childElementNodesLength = childElementNodes.length;
  294.  
  295. // If no element nodes were found, exit
  296. if (!childElementNodesLength) return;
  297.  
  298. // Make sure that the CSS-classnames cache has enough entries to cover
  299. // the number of child nodes
  300. if (childElementNodesLength > this.cacheLevel)
  301. {
  302. this.fillCacheTo(childElementNodesLength);
  303. }
  304.  
  305. var lastIndex = childElementNodesLength - 1; // index of the last element node
  306.  
  307. /* ============= /End Code Block "traverseChildren.A" ================ */
  308.  
  309. switch (childElementNodesLength)
  310. {
  311. case 0: return;
  312. break;
  313.  
  314. case 1:
  315. /* Take care of the only element */
  316.  
  317. var onlyElement = childElementNodes[0];
  318. this.removeMultipleClassNames(onlyElement, this.classNamesArray, this.classNameSubstringsArray);
  319.  
  320. // argument syntax: node to act upon, current index, boolean for whether isLast
  321. this._addOffspringClassNames( onlyElement, lastIndex, true );
  322.  
  323. onlyElement = null; // prevent memory leaks
  324.  
  325. break;
  326.  
  327. default:
  328. /* Take care of the first element */
  329.  
  330. var firstElement = childElementNodes[0];
  331. this.removeMultipleClassNames(firstElement, this.classNamesArray, this.classNameSubstringsArray);
  332.  
  333. // argument syntax: node to act upon, current index, boolean for whether isLast
  334. this._addOffspringClassNames( firstElement, 0, false );
  335.  
  336. firstElement = null; // prevent memory leaks
  337.  
  338. /* Take care of the last element */
  339.  
  340. var lastElement = childElementNodes[lastIndex];
  341. this.removeMultipleClassNames(lastElement, this.classNamesArray, this.classNameSubstringsArray);
  342.  
  343. // argument syntax: node to act upon, current index, boolean for whether isLast
  344. this._addOffspringClassNames( lastElement , lastIndex, true );
  345.  
  346. lastElement = null; // prevent memory leaks
  347.  
  348. break;
  349.  
  350. } // end of switch statement for childElementNodesLength
  351.  
  352. // Lastly, loop over all the childern elements
  353. for (var i = 0; i < childElementNodesLength; i++)
  354. {
  355. this.traverseChildren( childElementNodes[i] );
  356. }
  357.  
  358. /* ============= Begin Code Block "traverseChildren.B" ================ */
  359.  
  360. // prevent memory leaks
  361. lastElement = null;
  362. parent = null;
  363.  
  364. /* ============= /End Code Block "traverseChildren.B" ================ */
  365.  
  366. }; // end of traverseChildren function definition
  367.  
  368. break;
  369.  
  370. } // end of switch-statement for configuration.runningMode
  371.  
  372. break;
  373.  
  374. case false: // shouldRemoveOldOffspringClassesFirst is false
  375.  
  376. switch (this.configuration.runningMode)
  377. {
  378. case 'full': // 'full' running mode and shouldRemoveOldOffspringClassesFirst is false
  379. this.traverseChildren = function(parent)
  380. {
  381. /* ============= Begin Code Block "traverseChildren.A" ================ */
  382.  
  383. // If the node has no children, exit
  384. if (!parent.childNodes.length) return;
  385.  
  386.  
  387. /* First, gather up all the element nodes */
  388. var childElementNodes = [];
  389.  
  390. var testNode = parent.childNodes[0]; // initialize
  391.  
  392. while (testNode)
  393. {
  394. if (testNode.nodeType == 1)
  395. {
  396. childElementNodes.push(testNode);
  397. }
  398. testNode = testNode.nextSibling;
  399. }
  400.  
  401. /*
  402. empty this variable to ensure that the JavaScript
  403. interpreter doesn't have to update the variable's
  404. nodelist as DOM changes are made
  405. */
  406. testNode = null;
  407.  
  408. var childElementNodesLength = childElementNodes.length;
  409.  
  410. // If no element nodes were found, exit
  411. if (!childElementNodesLength) return;
  412.  
  413. // Make sure that the CSS-classnames cache has enough entries to cover
  414. // the number of child nodes
  415. if (childElementNodesLength > this.cacheLevel)
  416. {
  417. this.fillCacheTo(childElementNodesLength);
  418. }
  419.  
  420. var lastIndex = childElementNodesLength - 1; // index of the last element node
  421.  
  422. /* ============= /End Code Block "traverseChildren.A" ================ */
  423.  
  424. // First, take care of all but the last element
  425. for (var i = 0; i < lastIndex; i++)
  426. {
  427. var currentElement = childElementNodes[i];
  428.  
  429. // argument syntax: node to act upon, current index, boolean for whether isLast
  430. this._addOffspringClassNames(currentElement, i, false);
  431. this.traverseChildren(currentElement);
  432. }
  433.  
  434. currentElement = null; // prevent memory leaks
  435.  
  436. /*
  437. Then, take care of the last one
  438. (this set of code isn't integrated into
  439. the for-loop above so as to avoid having
  440. an addiitional if-statement inside there)
  441. */
  442. var lastElement = childElementNodes[lastIndex];
  443.  
  444. this._addOffspringClassNames(lastElement, lastIndex, true);
  445. this.traverseChildren(lastElement);
  446. lastElement = null; // prevent memory leaks
  447.  
  448. /* ============= Begin Code Block "traverseChildren.B" ================ */
  449.  
  450. // prevent memory leaks
  451. lastElement = null;
  452. parent = null;
  453.  
  454. /* ============= /End Code Block "traverseChildren.B" ================ */
  455.  
  456. }; // end of traverseChildren function definition
  457. break;
  458.  
  459. case 'light': // 'light' running mode and shouldRemoveOldOffspringClassesFirst is false
  460. this.traverseChildren = function(parent)
  461. {
  462. /* ============= Begin Code Block "traverseChildren.A" ================ */
  463.  
  464. // If the node has no children, exit
  465. if (!parent.childNodes.length) return;
  466.  
  467.  
  468. /* First, gather up all the element nodes */
  469. var childElementNodes = [];
  470.  
  471. var testNode = parent.childNodes[0]; // initialize
  472.  
  473. while (testNode)
  474. {
  475. if (testNode.nodeType == 1)
  476. {
  477. childElementNodes.push(testNode);
  478. }
  479. testNode = testNode.nextSibling;
  480. }
  481.  
  482. /*
  483. empty this variable to ensure that the JavaScript
  484. interpreter doesn't have to update the variable's
  485. nodelist as DOM changes are made
  486. */
  487. testNode = null;
  488.  
  489. var childElementNodesLength = childElementNodes.length;
  490.  
  491. // If no element nodes were found, exit
  492. if (!childElementNodesLength) return;
  493.  
  494. // Make sure that the CSS-classnames cache has enough entries to cover
  495. // the number of child nodes
  496. if (childElementNodesLength > this.cacheLevel)
  497. {
  498. this.fillCacheTo(childElementNodesLength);
  499. }
  500.  
  501. var lastIndex = childElementNodesLength - 1; // index of the last element node
  502.  
  503. /* ============= /End Code Block "traverseChildren.A" ================ */
  504.  
  505. switch (childElementNodesLength)
  506. {
  507. case 0: break;
  508.  
  509. case 1:
  510. /* Take care of the only element */
  511.  
  512. // argument syntax: node to act upon, current index, boolean for whether isLast
  513. this._addOffspringClassNames( childElementNodes[0], lastIndex, true );
  514.  
  515. // Lastly, loop over all the childern elements
  516. for (var i = 0; i < childElementNodesLength; i++)
  517. {
  518. this.traverseChildren( childElementNodes[i] );
  519. }
  520.  
  521. break;
  522.  
  523. default:
  524. /* Take care of the first element */
  525.  
  526. // argument syntax: node to act upon, current index, boolean for whether isLast
  527. this._addOffspringClassNames( childElementNodes[0], 0, false );
  528.  
  529. /* Take care of the last element */
  530.  
  531. // argument syntax: node to act upon, current index, boolean for whether isLast
  532. this._addOffspringClassNames( childElementNodes[lastIndex] , lastIndex, true );
  533.  
  534. // Lastly, loop over all the childern elements
  535. for (var i = 0; i < childElementNodesLength; i++)
  536. {
  537. this.traverseChildren( childElementNodes[i] );
  538. }
  539.  
  540. break;
  541. }
  542.  
  543. /* ============= Begin Code Block "traverseChildren.B" ================ */
  544.  
  545. // prevent memory leaks
  546. lastElement = null;
  547. parent = null;
  548.  
  549. /* ============= /End Code Block "traverseChildren.B" ================ */
  550.  
  551. }; // end of traverseChildren function definition
  552.  
  553. break;
  554. } // end of switch-statement for configuration.runningMode
  555.  
  556. break;
  557.  
  558. } // end of switch-statement for configuration.shouldRemoveOldOffspringClassesFirst
  559.  
  560. }, // end of defineTraverseChildrenFunction
  561.  
  562. // Recursive
  563.  
  564. /*
  565. If "shouldRemoveOldOffspringClassesFirst" is deined and set to true
  566. (it's optional), traverseChildren will remove old Offspring-related
  567. classes before applying new ones to a node. This could be useful
  568. for reapplying classes if the DOM is rejiggered.
  569. */
  570.  
  571. traverseChildren: function(parent) {
  572.  
  573. /* ============= Begin Code Block "traverseChildren.A" ================ */
  574.  
  575. // If the node has no children, exit
  576. if (!parent.childNodes.length) return;
  577.  
  578.  
  579. /* First, gather up all the element nodes */
  580. var childElementNodes = [];
  581.  
  582. var testNode = parent.childNodes[0]; // initialize
  583.  
  584. while (testNode)
  585. {
  586. if (testNode.nodeType == 1)
  587. {
  588. childElementNodes.push(testNode);
  589. }
  590. testNode = testNode.nextSibling;
  591. }
  592.  
  593. /*
  594. empty this variable to ensure that the JavaScript
  595. interpreter doesn't have to update the variable's
  596. nodelist as DOM changes are made
  597. */
  598. testNode = null;
  599.  
  600. var childElementNodesLength = childElementNodes.length;
  601.  
  602. // If no element nodes were found, exit
  603. if (!childElementNodesLength) return;
  604.  
  605. // Make sure that the CSS-classnames cache has enough entries to cover
  606. // the number of child nodes
  607. if (childElementNodesLength > this.cacheLevel)
  608. {
  609. this.fillCacheTo(childElementNodesLength);
  610. }
  611.  
  612. var lastIndex = childElementNodesLength - 1; // index of the last element node
  613.  
  614. /* ============= /End Code Block "traverseChildren.A" ================ */
  615.  
  616.  
  617. /* ==== Add the classes ====== */
  618.  
  619. this._childrenIterator(childElementNodes, childElementNodesLength, lastIndex);
  620.  
  621.  
  622. /* ============= Begin Code Block "traverseChildren.B" ================ */
  623.  
  624. // prevent memory leaks
  625. lastElement = null;
  626. parent = null;
  627.  
  628. /* ============= /End Code Block "traverseChildren.B" ================ */
  629.  
  630. },
  631.  
  632. /*
  633. This function adds the Offspring classnames to a given element,
  634. given its position among it siblings (with zero being "first")
  635. and whether it's the last element in its set.
  636. */
  637. _addOffspringClassNames: function(element, index, isLastElement) {
  638.  
  639. index++; // normalize since the arrays are indexed with a "1" starting point
  640.  
  641. // Steps if the element has no existing classnames...
  642.  
  643. if ((!element.className) || (!element.className.length))
  644. {
  645. switch (isLastElement)
  646. {
  647. case false: // it isn't the last element
  648. element.className = this.regularHashTable[index];
  649. return;
  650. break;
  651.  
  652. case true: // it is the last element
  653. element.className = this.lastChildHashTable[index];
  654. return;
  655. break;
  656.  
  657. } // end of isLastElement switch-statement
  658.  
  659. } // end of if-statement for checking whether the element has no existing className
  660.  
  661. // At this point, the incoming element already has className(s)
  662.  
  663. switch (isLastElement)
  664. {
  665. case false: // it isn't the last element
  666. var applicableClassNames = this.regularHashTableArray[index];
  667. break;
  668.  
  669. case true: // it is the last element
  670. var applicableClassNames = this.lastChildHashTableArray[index];
  671. break;
  672.  
  673. } // end of isLastElement switch-statement
  674.  
  675. var originalClassNames = element.className.split(' ');
  676.  
  677. var classNamesToAdd = originalClassNames; // initialize
  678.  
  679. for (var i = 0, applicableClassNamesLength = applicableClassNames.length; i < applicableClassNamesLength; i++)
  680. {
  681. var alreadyThere = false; // boolean for whether a given class name is already assigned to the element
  682.  
  683. var testApplicableClassName = applicableClassNames[i];
  684.  
  685. for (var j = 0, originalClassNamesLength = originalClassNames.length; j < originalClassNamesLength; j++)
  686. {
  687. if (originalClassNames[j] == testApplicableClassName)
  688. {
  689. alreadyThere = true;
  690. break;
  691. } // end of if-statement for checking if the element already has a given className
  692.  
  693. } // end of the originalClassNames for-loop
  694.  
  695. if (!alreadyThere)
  696. {
  697. classNamesToAdd.push(testApplicableClassName);
  698. }
  699.  
  700. } // end of applicableClassNames for-loop
  701.  
  702.  
  703. // Then, after checking over the element's existing classNames, add the new version
  704. element.className = classNamesToAdd.join(' ');
  705. element = null; // prevent memory leaks
  706.  
  707. return;
  708.  
  709. }, // end of _addOffspringClassNames()
  710.  
  711. /* Maintenance note for defineFillCacheToFunction:
  712.  
  713. [Aside: This is basically conveys the same idea as the comment above
  714. defineTraverseChildrenFunction. So, if you're read that one, you
  715. probably already have the basic idea of what's going on here.]
  716.  
  717. There are several blocks of code that are marked off as "fillCacheTo.A"
  718. or "fillCacheTo.B" -- each of these are identical, respectively. (That is,
  719. all "fillCacheTo.A" blocks are the same and all "fillCacheTo.B" are
  720. the same.)
  721.  
  722. So, why not just create a function where the code can be kept in one place?
  723. While normally a sensible idea, I decided against that approach only so
  724. that the speed hits associated with the creation of the function stack
  725. could be averted. At the same time, I didn't want to compromise
  726. the code's maintainability; so, if any block needs to be updated, they
  727. can all be kept in sync with some basic copy-n-pasting from one
  728. block to the next.
  729. */
  730.  
  731.  
  732. /* This defines the internal loop function for fillCacheTo,
  733. depending on how the configuration options are set */
  734. defineFillCacheToFunction: function() {
  735.  
  736. switch (this.configuration.runningMode)
  737. {
  738. case 'full': // 'full' running mode
  739. this.fillCacheTo = function(fillAmount)
  740. {
  741. /* ============= Begin Code Block "fillCacheTo.A" ================ */
  742.  
  743. var fillAmount = fillAmount || 15; // default value
  744.  
  745. if (!this.cacheLevel) this.cacheLevel = 0; // set this to a default value if needed
  746.  
  747. // If the cache level is already full enough, exit
  748. if (this.cacheLevel >= fillAmount) return;
  749.  
  750. var startingPoint = this.cacheLevel++;
  751.  
  752. /* ============= /End Code Block "fillCacheTo.A" ================ */
  753.  
  754. var isOdd = !((startingPoint % 2) == 0); // initialize
  755.  
  756. // cache these object name resolutions
  757. var firstChildClass = this.firstChildClass;
  758. var lastChildClass = this.lastChildClass;
  759. var oddChildClass = this.oddChildClass;
  760. var evenChildClass = this.evenChildClass;
  761. var onlyChildClass = this.onlyChildClass;
  762. var nthChildClassPrefix = this.nthChildClassPrefix;
  763.  
  764. for (var i = startingPoint; i <= fillAmount; i++)
  765. {
  766. this.nthChildren[i] = [nthChildClassPrefix, i].join('');
  767.  
  768. var nthChildrenI = this.nthChildren[i]; // cache this look-up
  769.  
  770. switch (i)
  771. {
  772. case 1:
  773. this.regularHashTableArray[i] = [firstChildClass, oddChildClass, nthChildrenI];
  774. this.lastChildHashTableArray[i] = [firstChildClass, oddChildClass, onlyChildClass, nthChildrenI, lastChildClass];
  775. break;
  776.  
  777. default:
  778. switch (isOdd)
  779. {
  780. case true: // "odd" is true
  781. this.regularHashTableArray[i] = [oddChildClass, nthChildrenI];
  782. this.lastChildHashTableArray[i] = [oddChildClass, nthChildrenI, lastChildClass];
  783. break;
  784.  
  785. case false: // "odd" is false
  786. this.regularHashTableArray[i] = [evenChildClass, nthChildrenI];
  787. this.lastChildHashTableArray[i] = [evenChildClass, nthChildrenI, lastChildClass];
  788. break;
  789.  
  790. } // end of isOdd switch-statement
  791.  
  792.  
  793. } // end of switch-statement for i
  794.  
  795. // Now make the joined versions for a given "i"
  796.  
  797. this.regularHashTable[i] = this.regularHashTableArray[i].join(' ');
  798. this.lastChildHashTable[i] = this.lastChildHashTableArray[i].join(' ');
  799.  
  800. isOdd = !isOdd; // flip the isOdd flag
  801.  
  802. } // end of filling for-loop
  803.  
  804. /* ============= Begin Code Block "fillCacheTo.B" ================ */
  805.  
  806. // If it got this far, the cacheLevel must made it to the fill amount, so update that
  807. this.cacheLevel = fillAmount;
  808.  
  809. /* ============= /End Code Block "fillCacheTo.B" ================ */
  810.  
  811. }; // end of fillCacheTo function definition
  812. break;
  813.  
  814. case 'light': // 'light' running mode
  815. this.fillCacheTo = function(fillAmount)
  816. {
  817. /* ============= Begin Code Block "fillCacheTo.A" ================ */
  818.  
  819. var fillAmount = fillAmount || 15; // default value
  820.  
  821. if (!this.cacheLevel) this.cacheLevel = 0; // set this to a default value if needed
  822.  
  823. // If the cache level is already full enough, exit
  824. if (this.cacheLevel >= fillAmount) return;
  825.  
  826. var startingPoint = this.cacheLevel++;
  827.  
  828. /* ============= /End Code Block "fillCacheTo.A" ================ */
  829.  
  830. // cache these object name resolutions
  831. var firstChildClass = this.firstChildClass;
  832. var lastChildClass = this.lastChildClass;
  833.  
  834. var onlyChildClass = this.onlyChildClass;
  835.  
  836. for (var i = startingPoint; i <= fillAmount; i++)
  837. {
  838.  
  839. switch (i)
  840. {
  841. case 1:
  842. this.regularHashTableArray[i] = [firstChildClass];
  843. this.lastChildHashTableArray[i] = [firstChildClass, onlyChildClass, lastChildClass];
  844. break;
  845.  
  846. default:
  847.  
  848. this.regularHashTableArray[i] = [];
  849. this.lastChildHashTableArray[i] = [lastChildClass];
  850.  
  851. } // end of switch-statement for i
  852.  
  853. // Now make the joined versions for a given "i"
  854.  
  855. this.regularHashTable[i] = this.regularHashTableArray[i].join(' ');
  856. this.lastChildHashTable[i] = this.lastChildHashTableArray[i].join(' ');
  857.  
  858. } // end of filling for-loop
  859.  
  860. /* ============= Begin Code Block "fillCacheTo.B" ================ */
  861.  
  862. // If it got this far, the cacheLevel must made it to the fill amount, so update that
  863. this.cacheLevel = fillAmount;
  864.  
  865. /* ============= /End Code Block "fillCacheTo.B" ================ */
  866.  
  867. }; // end of fillCacheTo function definition
  868. break;
  869.  
  870. } // end of switch statement for this.configuration.runningMode
  871.  
  872. }, // end of defineFillCacheToFunction
  873.  
  874. // This fills the className caches to the specified amount
  875. fillCacheTo: function(fillAmount) {
  876.  
  877. /* ============= Begin Code Block "fillCacheTo.A" ================ */
  878.  
  879. var fillAmount = fillAmount || 15; // default value
  880.  
  881. if (!this.cacheLevel) this.cacheLevel = 0; // set this to a default value if needed
  882.  
  883. // If the cache level is already full enough, exit
  884. if (this.cacheLevel >= fillAmount) return;
  885.  
  886. var startingPoint = this.cacheLevel++;
  887.  
  888. /* ============= /End Code Block "fillCacheTo.A" ================ */
  889.  
  890. this._fillCacheToIterator(startingPoint, fillAmount);
  891.  
  892. /* ============= Begin Code Block "fillCacheTo.B" ================ */
  893.  
  894. // If it got this far, the cacheLevel must made it to the fill amount, so update that
  895. this.cacheLevel = fillAmount;
  896.  
  897. /* ============= /End Code Block "fillCacheTo.B" ================ */
  898.  
  899. }, // end of fillCacheTo()
  900.  
  901. /* Returns true if testString is found in the array,
  902. or returns false otherwise */
  903. _checkIfStringFoundInArray: function(testString, testArray) {
  904.  
  905. // Loop through all testArray[] and if/when there's a match, return true
  906. for (var i = 0, len=testArray.length; i < len; i++)
  907. {
  908. if (testString == testArray[i]) return true;
  909. }
  910.  
  911. // If it got this far, it must not have found the string in the array
  912. return false;
  913.  
  914. }, // end of _checkIfStringFoundInArray
  915.  
  916. /* Returns true if the beginning of testString matches one of the substrings
  917. in the array. Otherwise, it returns false.
  918.  
  919. For example, given the array ['plum', 'orange', 'pine'] and
  920. the testString 'pineapples', the function would return true. However,
  921. given the testString 'range', it would return false (since none of
  922. the strings in the array start with 'range')
  923. */
  924. _checkIfStringMatchInSubstringArray: function(testString, testArray) {
  925.  
  926. // Loop through all testArray[] and if/when there's a match, return true
  927. for (var i = 0, len=testArray.length; i < len; i++)
  928. {
  929. var currentArrayItem = testArray[i];
  930.  
  931. /* string.substr() accepts two parameters:
  932. - The starting point of the substring
  933. - The length of the substring
  934. */
  935. var testSubstring = testString.substr(0, currentArrayItem.length);
  936.  
  937. if (testSubstring == currentArrayItem) return true;
  938. }
  939.  
  940. // If it got this far, it must not have found the string in the array
  941. return false;
  942.  
  943. }, // end of _checkIfStringMatchInSubstringArray
  944.  
  945. /*
  946. This removes multiple classnames from an element. It does this by
  947. checking each of an element's class names against
  948. classNameStrings[] for an exact match and, if a given class name
  949. didn't match there, it's then checked to see if it matches
  950. as a substring against classNAmeSubstrings[].
  951.  
  952. Of note, when comparing substrings, this intentionally only compares
  953. the beginning of the strings for a match. So, for example, "ora" would
  954. match as a substring of "orange", but "range" would not match as a substring
  955. of "orange". It was done this way because that was the only type of substring-
  956. comparison that was needed in this case, and a more thorough substring
  957. comparison would needlesslly use processor time.
  958. */
  959. removeMultipleClassNames: function(element, classNameStrings, classNameSubstrings) {
  960.  
  961. if (!element) return;
  962. var newClassName = '';
  963. var classNamesArray = element.className.split(' ');
  964.  
  965. for (var i = 0, len = classNamesArray.length; i < len; i++)
  966. {
  967. var currentClassName = classNamesArray[i];
  968.  
  969. var isStringInClassNameStrings = this._checkIfStringFoundInArray(currentClassName, classNameStrings);
  970.  
  971. if (isStringInClassNameStrings) continue;
  972.  
  973. var isStringMatchingClassNameSubstrings = this._checkIfStringMatchInSubstringArray(currentClassName, classNameSubstrings);
  974.  
  975. if (isStringMatchingClassNameSubstrings) continue;
  976.  
  977. // If it got this far, it must not have matched any of the potential classNameStrings
  978. // or classNameRegexes, so add the current iteration to the neClassName
  979.  
  980. if (i > 0) newClassName = newClassName + ' ';
  981. newClassName = newClassName + currentClassName;
  982.  
  983. }
  984. element.className = newClassName;
  985.  
  986. }, // end of removeMultipleClassNames
  987.  
  988.  
  989. /*
  990. *
  991. * ContentLoaded.js
  992. *
  993. * Author: Diego Perini (diego.perini at gmail.com)
  994. * Summary: Cross-browser wrapper for DOMContentLoaded
  995. * Updated: 05/10/2007
  996. * License: GPL/CC
  997. * Version: 1.0
  998. *
  999. * http://javascript.nwbox.com/ContentLoaded/
  1000. *
  1001. * Notes:
  1002. *
  1003. * based on code by Dean Edwards and John Resig
  1004. * http://dean.edwards.name/weblog/2006/06/again/
  1005. *
  1006. *
  1007. */
  1008.  
  1009. /*
  1010. * Example call, in this case:
  1011.  
  1012. Offspring.ContentLoaded(window,
  1013. function () {
  1014. document.body.style.backgroundColor = 'green';
  1015. }
  1016. );
  1017. *
  1018. */
  1019.  
  1020. // @w window reference
  1021. // @f function reference
  1022. ContentLoaded: function (w, fn) {
  1023. var d = w.document,
  1024. u = w.navigator.userAgent.toLowerCase();
  1025.  
  1026. function init(e) {
  1027. if (!arguments.callee.done) {
  1028. arguments.callee.done = true;
  1029. fn(e);
  1030. }
  1031. }
  1032.  
  1033. // konqueror/safari
  1034. if (/khtml|webkit/.test(u)) {
  1035.  
  1036. (function () {
  1037. if (/complete|loaded/.test(d.readyState)) {
  1038. init('poll');
  1039. } else {
  1040. setTimeout(arguments.callee, 10);
  1041. }
  1042. })();
  1043.  
  1044. // internet explorer all versions
  1045. } else if (/msie/.test(u) && !w.opera) {
  1046.  
  1047. (function () {
  1048. try {
  1049. d.documentElement.doScroll('left');
  1050. } catch (e) {
  1051. setTimeout(arguments.callee, 10);
  1052. return;
  1053. }
  1054. init('poll');
  1055. })();
  1056. d.attachEvent('onreadystatechange',
  1057. function (e) {
  1058. if (d.readyState == 'complete') {
  1059. d.detachEvent('on'+e.type, arguments.callee);
  1060. init(e.type);
  1061. }
  1062. }
  1063. );
  1064.  
  1065. // browsers having native DOMContentLoaded
  1066. } else if (d.addEventListener &&
  1067. (/gecko/.test(u) && parseFloat(u.split('rv:')[1]) >= 1.8) ||
  1068. (/opera/.test(u) && parseFloat(u.split('opera ')[1]) > 9)) {
  1069.  
  1070. d.addEventListener('DOMContentLoaded',
  1071. function (e) {
  1072. this.removeEventListener(e.type, arguments.callee, false);
  1073. init(e.type);
  1074. }, false
  1075. );
  1076.  
  1077. // fallback to last resort
  1078. } else {
  1079.  
  1080. // from Simon Willison
  1081. var oldonload = w.onload;
  1082. w.onload = function (e) {
  1083. if (typeof oldonload == 'function') {
  1084. oldonload(e || w.event);
  1085. }
  1086. init((e || w.event).type);
  1087. };
  1088.  
  1089. }
  1090. } // end of ContentLoaded
  1091.  
  1092. }
  1093.  
  1094.  
  1095. // Kick off
  1096. offspring.init();

Report this snippet  

You need to login to post a comment.