Dashboard sipadu mbip
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

aui-parse-content-debug.js 7.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. AUI.add('aui-parse-content', function(A) {
  2. /**
  3. * The ParseContent Utility - Parse the content of a Node so that all of the
  4. * javascript contained in that Node will be executed according to the order
  5. * that it appears.
  6. *
  7. * @module aui-parse-content
  8. */
  9. /*
  10. * NOTE: The inspiration of ParseContent cames from the "Caridy Patino" Node Dispatcher Plugin
  11. * http://github.com/caridy/yui3-gallery/blob/master/src/gallery-dispatcher/
  12. */
  13. var L = A.Lang,
  14. isString = L.isString,
  15. DOC = A.config.doc,
  16. APPEND = 'append',
  17. DOCUMENT_ELEMENT = 'documentElement',
  18. FIRST_CHILD = 'firstChild',
  19. HEAD = 'head',
  20. HOST = 'host',
  21. INNER_HTML = 'innerHTML',
  22. PADDING_NODE = '<div>_</div>',
  23. PARSE_CONTENT = 'ParseContent',
  24. QUEUE = 'queue',
  25. SCRIPT = 'script',
  26. SEMICOLON = ';',
  27. SRC = 'src',
  28. SCRIPT_TYPES = {
  29. '': 1,
  30. 'text/javascript': 1
  31. };
  32. /**
  33. * A base class for ParseContent, providing:
  34. * <ul>
  35. * <li>After plug ParseContent on a A.Node instance the javascript chunks will be executed (remote and inline scripts)</li>
  36. * <li>All the javascripts within a content will be executed according to the order of apparition</li>
  37. * </ul>
  38. *
  39. * <p><strong>NOTE:</strong> For performance reasons on DOM manipulation,
  40. * ParseContent only parses the content passed to the
  41. * <a href="Node.html#method_setContent">setContent</a>,
  42. * <a href="Node.html#method_prepend">prepend</a> and
  43. * <a href="Node.html#method_append">append</a> methods.</p>
  44. *
  45. * Quick Example:<br/>
  46. *
  47. * <pre><code>node.plug(A.Plugin.ParseContent);</code></pre>
  48. *
  49. * Check the list of <a href="ParseContent.html#configattributes">Configuration Attributes</a> available for
  50. * ParseContent.
  51. *
  52. * @param config {Object} Object literal specifying widget configuration properties.
  53. *
  54. * @class ParseContent
  55. * @constructor
  56. * @extends Plugin.Base
  57. */
  58. var ParseContent = A.Component.create(
  59. {
  60. /**
  61. * Static property provides a string to identify the class.
  62. *
  63. * @property ParseContent.NAME
  64. * @type String
  65. * @static
  66. */
  67. NAME: PARSE_CONTENT,
  68. /**
  69. * Static property provides a string to identify the namespace.
  70. *
  71. * @property ParseContent.NS
  72. * @type String
  73. * @static
  74. */
  75. NS: PARSE_CONTENT,
  76. /**
  77. * Static property used to define the default attribute
  78. * configuration for the ParseContent.
  79. *
  80. * @property ParseContent.ATTRS
  81. * @type Object
  82. * @static
  83. */
  84. ATTRS: {
  85. queue: {
  86. value: null
  87. }
  88. },
  89. EXTENDS: A.Plugin.Base,
  90. prototype: {
  91. /**
  92. * Construction logic executed during ParseContent instantiation. Lifecycle.
  93. *
  94. * @method initializer
  95. * @protected
  96. */
  97. initializer: function() {
  98. var instance = this;
  99. ParseContent.superclass.initializer.apply(this, arguments);
  100. instance.set(
  101. QUEUE,
  102. new A.AsyncQueue()
  103. );
  104. instance._bindAOP();
  105. },
  106. /**
  107. * Global eval the <data>data</data> passed.
  108. *
  109. * @method globalEval
  110. * @param {String} data JavaScript String.
  111. */
  112. globalEval: function(data) {
  113. var doc = A.getDoc();
  114. var head = doc.one(HEAD) || doc.get(DOCUMENT_ELEMENT);
  115. // NOTE: A.Node.create('<script></script>') doesn't work correctly on Opera
  116. var newScript = DOC.createElement(SCRIPT);
  117. newScript.type = 'text/javascript';
  118. if (data) {
  119. // NOTE: newScript.set(TEXT, data) breaks on IE, YUI BUG.
  120. newScript.text = L.trim(data);
  121. }
  122. head.appendChild(newScript).remove(); //removes the script node immediately after executing it
  123. },
  124. /**
  125. * Extract the <code>script</code> tags from the string content and
  126. * evaluate the chunks.
  127. *
  128. * @method parseContent
  129. * @param {String} content HTML string
  130. * @return {String}
  131. */
  132. parseContent: function(content) {
  133. var instance = this;
  134. var output = instance._clean(content);
  135. instance._dispatch(output);
  136. return output;
  137. },
  138. /**
  139. * Add inline script data to the queue.
  140. *
  141. * @method _addInlineScript
  142. * @param {String} data The script content which should be added to the queue
  143. * @protected
  144. */
  145. _addInlineScript: function(data) {
  146. var instance = this;
  147. instance.get(QUEUE).add(
  148. {
  149. args: data,
  150. context: instance,
  151. fn: instance.globalEval,
  152. timeout: 0
  153. }
  154. );
  155. },
  156. /**
  157. * Bind listeners on the <code>insert</code> and <code>setContent</code>
  158. * methods of the Node instance where you are plugging the ParseContent.
  159. * These listeners are responsible for intercept the HTML passed and parse
  160. * them.
  161. *
  162. * @method _bindAOP
  163. * @protected
  164. */
  165. _bindAOP: function() {
  166. var instance = this;
  167. var cleanFirstArg = function(content) {
  168. var args = Array.prototype.slice.call(arguments);
  169. var output = instance.parseContent(content);
  170. // replace the first argument with the clean fragment
  171. args.splice(0, 1, output.fragment);
  172. return new A.Do.AlterArgs(null, args);
  173. };
  174. this.doBefore('insert', cleanFirstArg);
  175. this.doBefore('replaceChild', cleanFirstArg);
  176. var cleanArgs = function(content) {
  177. var output = instance.parseContent(content);
  178. return new A.Do.AlterArgs(null, [output.fragment]);
  179. };
  180. this.doBefore('replace', cleanArgs);
  181. this.doBefore('setContent', cleanArgs);
  182. },
  183. /**
  184. * Create an HTML fragment with the String passed, extract all the script
  185. * tags and return an Object with a reference for the extracted scripts and
  186. * the fragment.
  187. *
  188. * @method clean
  189. * @param {String} content HTML content.
  190. * @protected
  191. * @return {Object}
  192. */
  193. _clean: function(content) {
  194. var output = {};
  195. var fragment = A.Node.create('<div></div>');
  196. // For PADDING_NODE, instead of fixing all tags in the content to be "XHTML"-style,
  197. // we make the firstChild be a valid non-empty tag, then we remove it later
  198. if (isString(content)) {
  199. content = PADDING_NODE + content;
  200. // create fragment from {String}
  201. A.DOM.addHTML(fragment, content, APPEND);
  202. }
  203. else {
  204. fragment.append(PADDING_NODE);
  205. // create fragment from {Y.Node | HTMLElement}
  206. fragment.append(content);
  207. }
  208. output.js = fragment.all(SCRIPT).filter(function(script) {
  209. return SCRIPT_TYPES[script.getAttribute('type').toLowerCase()];
  210. });
  211. output.js.each(
  212. function(node, i) {
  213. node.remove();
  214. }
  215. );
  216. // remove PADDING_NODE
  217. fragment.get(FIRST_CHILD).remove();
  218. output.fragment = fragment.get('childNodes').toFrag();
  219. return output;
  220. },
  221. /**
  222. * Loop trough all extracted <code>script</code> tags and evaluate them.
  223. *
  224. * @method _dispatch
  225. * @param {Object} output Object containing the reference for the fragment and the extracted <code>script</code> tags.
  226. * @protected
  227. * @return {String}
  228. */
  229. _dispatch: function(output) {
  230. var instance = this;
  231. var queue = instance.get(QUEUE);
  232. var scriptContent = [];
  233. output.js.each(function(node, i) {
  234. var src = node.get(SRC);
  235. if (src) {
  236. if (scriptContent.length) {
  237. instance._addInlineScript(scriptContent.join(SEMICOLON));
  238. scriptContent.length = 0;
  239. }
  240. queue.add({
  241. autoContinue: false,
  242. fn: function () {
  243. A.Get.script(src, {
  244. onEnd: function (o) {
  245. o.purge(); //removes the script node immediately after executing it
  246. queue.run();
  247. }
  248. });
  249. },
  250. timeout: 0
  251. });
  252. }
  253. else {
  254. var dom = node._node;
  255. scriptContent.push(dom.text || dom.textContent || dom.innerHTML || '');
  256. }
  257. });
  258. if (scriptContent.length) {
  259. instance._addInlineScript(scriptContent.join(SEMICOLON));
  260. }
  261. queue.run();
  262. }
  263. }
  264. }
  265. );
  266. A.namespace('Plugin').ParseContent = ParseContent;
  267. }, '@VERSION@' ,{requires:['async-queue','aui-base','plugin'], skinnable:false});