PageRenderTime 75ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 1ms

/static/scripts/jquery.wymeditor.js

https://bitbucket.org/cistrome/cistrome-harvard/
JavaScript | 2092 lines | 1431 code | 272 blank | 389 comment | 156 complexity | e0b3fb4aa4dd7c540f46f4b312cca967 MD5 | raw file
  1. /**
  2. * @version 0.5-rc1
  3. *
  4. * WYMeditor : what you see is What You Mean web-based editor
  5. * Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/
  6. * Dual licensed under the MIT (MIT-license.txt)
  7. * and GPL (GPL-license.txt) licenses.
  8. *
  9. * For further information visit:
  10. * http://www.wymeditor.org/
  11. *
  12. * File: jquery.wymeditor.js
  13. *
  14. * Main JS file with core classes and functions.
  15. * See the documentation for more info.
  16. *
  17. * About: authors
  18. *
  19. * Jean-Francois Hovinne (jf.hovinne a-t wymeditor dotorg)
  20. * Volker Mische (vmx a-t gmx dotde)
  21. * Scott Lewis (lewiscot a-t gmail dotcom)
  22. * Bermi Ferrer (wymeditor a-t bermi dotorg)
  23. * Daniel Reszka (d.reszka a-t wymeditor dotorg)
  24. * Jonatan Lundin (jonatan.lundin _at_ gmail.com)
  25. */
  26. /*
  27. Namespace: WYMeditor
  28. Global WYMeditor namespace.
  29. */
  30. if(!WYMeditor) var WYMeditor = {};
  31. //Wrap the Firebug console in WYMeditor.console
  32. (function() {
  33. if ( !window.console || !console.firebug ) {
  34. var names = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml",
  35. "group", "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"];
  36. WYMeditor.console = {};
  37. for (var i = 0; i < names.length; ++i)
  38. WYMeditor.console[names[i]] = function() {}
  39. } else WYMeditor.console = window.console;
  40. })();
  41. jQuery.extend(WYMeditor, {
  42. /*
  43. Constants: Global WYMeditor constants.
  44. VERSION - Defines WYMeditor version.
  45. INSTANCES - An array of loaded WYMeditor.editor instances.
  46. STRINGS - An array of loaded WYMeditor language pairs/values.
  47. SKINS - An array of loaded WYMeditor skins.
  48. NAME - The "name" attribute.
  49. INDEX - A string replaced by the instance index.
  50. WYM_INDEX - A string used to get/set the instance index.
  51. BASE_PATH - A string replaced by WYMeditor's base path.
  52. SKIN_PATH - A string replaced by WYMeditor's skin path.
  53. WYM_PATH - A string replaced by WYMeditor's main JS file path.
  54. SKINS_DEFAULT_PATH - The skins default base path.
  55. SKINS_DEFAULT_CSS - The skins default CSS file.
  56. LANG_DEFAULT_PATH - The language files default path.
  57. IFRAME_BASE_PATH - A string replaced by the designmode iframe's base path.
  58. IFRAME_DEFAULT - The iframe's default base path.
  59. JQUERY_PATH - A string replaced by the computed jQuery path.
  60. DIRECTION - A string replaced by the text direction (rtl or ltr).
  61. LOGO - A string replaced by WYMeditor logo.
  62. TOOLS - A string replaced by the toolbar's HTML.
  63. TOOLS_ITEMS - A string replaced by the toolbar items.
  64. TOOL_NAME - A string replaced by a toolbar item's name.
  65. TOOL_TITLE - A string replaced by a toolbar item's title.
  66. TOOL_CLASS - A string replaced by a toolbar item's class.
  67. CLASSES - A string replaced by the classes panel's HTML.
  68. CLASSES_ITEMS - A string replaced by the classes items.
  69. CLASS_NAME - A string replaced by a class item's name.
  70. CLASS_TITLE - A string replaced by a class item's title.
  71. CONTAINERS - A string replaced by the containers panel's HTML.
  72. CONTAINERS_ITEMS - A string replaced by the containers items.
  73. CONTAINER_NAME - A string replaced by a container item's name.
  74. CONTAINER_TITLE - A string replaced by a container item's title.
  75. CONTAINER_CLASS - A string replaced by a container item's class.
  76. HTML - A string replaced by the HTML view panel's HTML.
  77. IFRAME - A string replaced by the designmode iframe.
  78. STATUS - A string replaced by the status panel's HTML.
  79. DIALOG_TITLE - A string replaced by a dialog's title.
  80. DIALOG_BODY - A string replaced by a dialog's HTML body.
  81. BODY - The BODY element.
  82. STRING - The "string" type.
  83. BODY,DIV,P,
  84. H1,H2,H3,H4,H5,H6,
  85. PRE,BLOCKQUOTE,
  86. A,BR,IMG,
  87. TABLE,TD,TH,
  88. UL,OL,LI - HTML elements string representation.
  89. CLASS,HREF,SRC,
  90. TITLE,ALT - HTML attributes string representation.
  91. DIALOG_LINK - A link dialog type.
  92. DIALOG_IMAGE - An image dialog type.
  93. DIALOG_TABLE - A table dialog type.
  94. DIALOG_PASTE - A 'Paste from Word' dialog type.
  95. BOLD - Command: (un)set selection to <strong>.
  96. ITALIC - Command: (un)set selection to <em>.
  97. CREATE_LINK - Command: open the link dialog or (un)set link.
  98. INSERT_IMAGE - Command: open the image dialog or insert an image.
  99. INSERT_TABLE - Command: open the table dialog.
  100. PASTE - Command: open the paste dialog.
  101. INDENT - Command: nest a list item.
  102. OUTDENT - Command: unnest a list item.
  103. TOGGLE_HTML - Command: display/hide the HTML view.
  104. FORMAT_BLOCK - Command: set a block element to another type.
  105. PREVIEW - Command: open the preview dialog.
  106. UNLINK - Command: unset a link.
  107. INSERT_UNORDEREDLIST- Command: insert an unordered list.
  108. INSERT_ORDEREDLIST - Command: insert an ordered list.
  109. MAIN_CONTAINERS - An array of the main HTML containers used in WYMeditor.
  110. BLOCKS - An array of the HTML block elements.
  111. KEY - Standard key codes.
  112. NODE - Node types.
  113. */
  114. VERSION : "0.5-rc1",
  115. INSTANCES : [],
  116. STRINGS : [],
  117. SKINS : [],
  118. NAME : "name",
  119. INDEX : "{Wym_Index}",
  120. WYM_INDEX : "wym_index",
  121. BASE_PATH : "{Wym_Base_Path}",
  122. CSS_PATH : "{Wym_Css_Path}",
  123. WYM_PATH : "{Wym_Wym_Path}",
  124. SKINS_DEFAULT_PATH : "skins/",
  125. SKINS_DEFAULT_CSS : "skin.css",
  126. SKINS_DEFAULT_JS : "skin.js",
  127. LANG_DEFAULT_PATH : "lang/",
  128. IFRAME_BASE_PATH : "{Wym_Iframe_Base_Path}",
  129. IFRAME_DEFAULT : "iframe/default/",
  130. JQUERY_PATH : "{Wym_Jquery_Path}",
  131. DIRECTION : "{Wym_Direction}",
  132. LOGO : "{Wym_Logo}",
  133. TOOLS : "{Wym_Tools}",
  134. TOOLS_ITEMS : "{Wym_Tools_Items}",
  135. TOOL_NAME : "{Wym_Tool_Name}",
  136. TOOL_TITLE : "{Wym_Tool_Title}",
  137. TOOL_CLASS : "{Wym_Tool_Class}",
  138. CLASSES : "{Wym_Classes}",
  139. CLASSES_ITEMS : "{Wym_Classes_Items}",
  140. CLASS_NAME : "{Wym_Class_Name}",
  141. CLASS_TITLE : "{Wym_Class_Title}",
  142. CONTAINERS : "{Wym_Containers}",
  143. CONTAINERS_ITEMS : "{Wym_Containers_Items}",
  144. CONTAINER_NAME : "{Wym_Container_Name}",
  145. CONTAINER_TITLE : "{Wym_Containers_Title}",
  146. CONTAINER_CLASS : "{Wym_Container_Class}",
  147. HTML : "{Wym_Html}",
  148. IFRAME : "{Wym_Iframe}",
  149. STATUS : "{Wym_Status}",
  150. DIALOG_TITLE : "{Wym_Dialog_Title}",
  151. DIALOG_BODY : "{Wym_Dialog_Body}",
  152. STRING : "string",
  153. BODY : "body",
  154. DIV : "div",
  155. P : "p",
  156. H1 : "h1",
  157. H2 : "h2",
  158. H3 : "h3",
  159. H4 : "h4",
  160. H5 : "h5",
  161. H6 : "h6",
  162. PRE : "pre",
  163. BLOCKQUOTE : "blockquote",
  164. A : "a",
  165. BR : "br",
  166. IMG : "img",
  167. TABLE : "table",
  168. TD : "td",
  169. TH : "th",
  170. UL : "ul",
  171. OL : "ol",
  172. LI : "li",
  173. CLASS : "class",
  174. HREF : "href",
  175. SRC : "src",
  176. TITLE : "title",
  177. ALT : "alt",
  178. DIALOG_LINK : "Link",
  179. DIALOG_IMAGE : "Image",
  180. DIALOG_TABLE : "Table",
  181. DIALOG_PASTE : "Paste_From_Word",
  182. BOLD : "Bold",
  183. ITALIC : "Italic",
  184. CREATE_LINK : "CreateLink",
  185. INSERT_IMAGE : "InsertImage",
  186. INSERT_TABLE : "InsertTable",
  187. INSERT_HTML : "InsertHTML",
  188. PASTE : "Paste",
  189. INDENT : "Indent",
  190. OUTDENT : "Outdent",
  191. TOGGLE_HTML : "ToggleHtml",
  192. FORMAT_BLOCK : "FormatBlock",
  193. PREVIEW : "Preview",
  194. UNLINK : "Unlink",
  195. INSERT_UNORDEREDLIST: "InsertUnorderedList",
  196. INSERT_ORDEREDLIST : "InsertOrderedList",
  197. MAIN_CONTAINERS : new Array("p","h1","h2","h3","h4","h5","h6","pre","blockquote"),
  198. BLOCKS : new Array("address", "blockquote", "div", "dl",
  199. "fieldset", "form", "h1", "h2", "h3", "h4", "h5", "h6", "hr",
  200. "noscript", "ol", "p", "pre", "table", "ul", "dd", "dt",
  201. "li", "tbody", "td", "tfoot", "th", "thead", "tr"),
  202. KEY : {
  203. BACKSPACE: 8,
  204. ENTER: 13,
  205. END: 35,
  206. HOME: 36,
  207. LEFT: 37,
  208. UP: 38,
  209. RIGHT: 39,
  210. DOWN: 40,
  211. CURSOR: new Array(37, 38, 39, 40),
  212. DELETE: 46
  213. },
  214. NODE : {
  215. ELEMENT: 1,
  216. ATTRIBUTE: 2,
  217. TEXT: 3
  218. },
  219. /*
  220. Class: WYMeditor.editor
  221. WYMeditor editor main class, instanciated for each editor occurrence.
  222. */
  223. editor : function(elem, options) {
  224. /*
  225. Constructor: WYMeditor.editor
  226. Initializes main values (index, elements, paths, ...)
  227. and call WYMeditor.editor.init which initializes the editor.
  228. Parameters:
  229. elem - The HTML element to be replaced by the editor.
  230. options - The hash of options.
  231. Returns:
  232. Nothing.
  233. See Also:
  234. <WYMeditor.editor.init>
  235. */
  236. //store the instance in the INSTANCES array and store the index
  237. this._index = WYMeditor.INSTANCES.push(this) - 1;
  238. //store the element replaced by the editor
  239. this._element = elem;
  240. //store the options
  241. this._options = options;
  242. //store the element's inner value
  243. this._html = jQuery(elem).val();
  244. //store the HTML option, if any
  245. if(this._options.html) this._html = this._options.html;
  246. //get or compute the base path (where the main JS file is located)
  247. this._options.basePath = this._options.basePath
  248. || this.computeBasePath();
  249. //get or set the skin path (where the skin files are located)
  250. this._options.skinPath = this._options.skinPath
  251. || this._options.basePath + WYMeditor.SKINS_DEFAULT_PATH
  252. + this._options.skin + '/';
  253. //get or compute the main JS file location
  254. this._options.wymPath = this._options.wymPath
  255. || this.computeWymPath();
  256. //get or set the language files path
  257. this._options.langPath = this._options.langPath
  258. || this._options.basePath + WYMeditor.LANG_DEFAULT_PATH;
  259. //get or set the designmode iframe's base path
  260. this._options.iframeBasePath = this._options.iframeBasePath
  261. || this._options.basePath + WYMeditor.IFRAME_DEFAULT;
  262. //get or compute the jQuery JS file location
  263. this._options.jQueryPath = this._options.jQueryPath
  264. || this.computeJqueryPath();
  265. //initialize the editor instance
  266. this.init();
  267. }
  268. });
  269. /********** JQUERY **********/
  270. /**
  271. * Replace an HTML element by WYMeditor
  272. *
  273. * @example jQuery(".wymeditor").wymeditor(
  274. * {
  275. *
  276. * }
  277. * );
  278. * @desc Example description here
  279. *
  280. * @name WYMeditor
  281. * @description WYMeditor is a web-based WYSIWYM XHTML editor
  282. * @param Hash hash A hash of parameters
  283. * @option Integer iExample Description here
  284. * @option String sExample Description here
  285. *
  286. * @type jQuery
  287. * @cat Plugins/WYMeditor
  288. * @author Jean-Francois Hovinne
  289. */
  290. jQuery.fn.wymeditor = function(options) {
  291. options = jQuery.extend({
  292. html: "",
  293. basePath: false,
  294. skinPath: false,
  295. wymPath: false,
  296. iframeBasePath: false,
  297. jQueryPath: false,
  298. styles: false,
  299. stylesheet: false,
  300. skin: "default",
  301. initSkin: true,
  302. loadSkin: true,
  303. lang: "en",
  304. direction: "ltr",
  305. boxHtml: "<div class='wym_box'>"
  306. + "<div class='wym_area_top'>"
  307. + WYMeditor.TOOLS
  308. + "</div>"
  309. + "<div class='wym_area_left'></div>"
  310. + "<div class='wym_area_right'>"
  311. + WYMeditor.CONTAINERS
  312. + WYMeditor.CLASSES
  313. + "</div>"
  314. + "<div class='wym_area_main'>"
  315. + WYMeditor.HTML
  316. + WYMeditor.IFRAME
  317. + WYMeditor.STATUS
  318. + "</div>"
  319. + "<div class='wym_area_bottom'>"
  320. + WYMeditor.LOGO
  321. + "</div>"
  322. + "</div>",
  323. logoHtml: "<a class='wym_wymeditor_link' "
  324. + "href='http://www.wymeditor.org/'>WYMeditor</a>",
  325. iframeHtml:"<div class='wym_iframe wym_section'>"
  326. + "<iframe "
  327. + "src='"
  328. + "/page/get_editor_iframe' "
  329. //+ WYMeditor.IFRAME_BASE_PATH
  330. //+ "wymiframe.html' "
  331. + "onload='this.contentWindow.parent.WYMeditor.INSTANCES["
  332. + WYMeditor.INDEX + "].initIframe(this)'"
  333. + "></iframe>"
  334. + "</div>",
  335. editorStyles: [],
  336. toolsHtml: "<div class='wym_tools wym_section'>"
  337. + "<h2>{Tools}</h2>"
  338. + "<ul>"
  339. + WYMeditor.TOOLS_ITEMS
  340. + "</ul>"
  341. + "</div>",
  342. toolsItemHtml: "<li class='"
  343. + WYMeditor.TOOL_CLASS
  344. + "'><a href='#' name='"
  345. + WYMeditor.TOOL_NAME
  346. + "' title='"
  347. + WYMeditor.TOOL_TITLE
  348. + "'>"
  349. + WYMeditor.TOOL_TITLE
  350. + "</a></li>",
  351. toolsItems: [
  352. {'name': 'Bold', 'title': 'Strong', 'css': 'wym_tools_strong'},
  353. {'name': 'Italic', 'title': 'Emphasis', 'css': 'wym_tools_emphasis'},
  354. {'name': 'Superscript', 'title': 'Superscript',
  355. 'css': 'wym_tools_superscript'},
  356. {'name': 'Subscript', 'title': 'Subscript',
  357. 'css': 'wym_tools_subscript'},
  358. {'name': 'InsertOrderedList', 'title': 'Ordered_List',
  359. 'css': 'wym_tools_ordered_list'},
  360. {'name': 'InsertUnorderedList', 'title': 'Unordered_List',
  361. 'css': 'wym_tools_unordered_list'},
  362. {'name': 'Indent', 'title': 'Indent', 'css': 'wym_tools_indent'},
  363. {'name': 'Outdent', 'title': 'Outdent', 'css': 'wym_tools_outdent'},
  364. {'name': 'Undo', 'title': 'Undo', 'css': 'wym_tools_undo'},
  365. {'name': 'Redo', 'title': 'Redo', 'css': 'wym_tools_redo'},
  366. {'name': 'CreateLink', 'title': 'Link', 'css': 'wym_tools_link'},
  367. {'name': 'Unlink', 'title': 'Unlink', 'css': 'wym_tools_unlink'},
  368. {'name': 'InsertImage', 'title': 'Image', 'css': 'wym_tools_image'},
  369. {'name': 'InsertTable', 'title': 'Table', 'css': 'wym_tools_table'},
  370. {'name': 'Paste', 'title': 'Paste_From_Word',
  371. 'css': 'wym_tools_paste'},
  372. {'name': 'ToggleHtml', 'title': 'HTML', 'css': 'wym_tools_html'},
  373. {'name': 'Preview', 'title': 'Preview', 'css': 'wym_tools_preview'}
  374. ],
  375. containersHtml: "<div class='wym_containers wym_section'>"
  376. + "<h2>{Containers}</h2>"
  377. + "<ul>"
  378. + WYMeditor.CONTAINERS_ITEMS
  379. + "</ul>"
  380. + "</div>",
  381. containersItemHtml:"<li class='"
  382. + WYMeditor.CONTAINER_CLASS
  383. + "'>"
  384. + "<a href='#' name='"
  385. + WYMeditor.CONTAINER_NAME
  386. + "'>"
  387. + WYMeditor.CONTAINER_TITLE
  388. + "</a></li>",
  389. containersItems: [
  390. {'name': 'P', 'title': 'Paragraph', 'css': 'wym_containers_p'},
  391. {'name': 'H1', 'title': 'Heading_1', 'css': 'wym_containers_h1'},
  392. {'name': 'H2', 'title': 'Heading_2', 'css': 'wym_containers_h2'},
  393. {'name': 'H3', 'title': 'Heading_3', 'css': 'wym_containers_h3'},
  394. {'name': 'H4', 'title': 'Heading_4', 'css': 'wym_containers_h4'},
  395. {'name': 'H5', 'title': 'Heading_5', 'css': 'wym_containers_h5'},
  396. {'name': 'H6', 'title': 'Heading_6', 'css': 'wym_containers_h6'},
  397. {'name': 'PRE', 'title': 'Preformatted', 'css': 'wym_containers_pre'},
  398. {'name': 'BLOCKQUOTE', 'title': 'Blockquote',
  399. 'css': 'wym_containers_blockquote'},
  400. {'name': 'TH', 'title': 'Table_Header', 'css': 'wym_containers_th'}
  401. ],
  402. classesHtml: "<div class='wym_classes wym_section'>"
  403. + "<h2>{Classes}</h2><ul>"
  404. + WYMeditor.CLASSES_ITEMS
  405. + "</ul></div>",
  406. classesItemHtml: "<li><a href='#' name='"
  407. + WYMeditor.CLASS_NAME
  408. + "'>"
  409. + WYMeditor.CLASS_TITLE
  410. + "</a></li>",
  411. classesItems: [],
  412. statusHtml: "<div class='wym_status wym_section'>"
  413. + "<h2>{Status}</h2>"
  414. + "</div>",
  415. htmlHtml: "<div class='wym_html wym_section'>"
  416. + "<h2>{Source_Code}</h2>"
  417. + "<textarea class='wym_html_val'></textarea>"
  418. + "</div>",
  419. boxSelector: ".wym_box",
  420. toolsSelector: ".wym_tools",
  421. toolsListSelector: " ul",
  422. containersSelector:".wym_containers",
  423. classesSelector: ".wym_classes",
  424. htmlSelector: ".wym_html",
  425. iframeSelector: ".wym_iframe iframe",
  426. iframeBodySelector:".wym_iframe",
  427. statusSelector: ".wym_status",
  428. toolSelector: ".wym_tools a",
  429. containerSelector: ".wym_containers a",
  430. classSelector: ".wym_classes a",
  431. htmlValSelector: ".wym_html_val",
  432. hrefSelector: ".wym_href",
  433. srcSelector: ".wym_src",
  434. titleSelector: ".wym_title",
  435. altSelector: ".wym_alt",
  436. textSelector: ".wym_text",
  437. rowsSelector: ".wym_rows",
  438. colsSelector: ".wym_cols",
  439. captionSelector: ".wym_caption",
  440. summarySelector: ".wym_summary",
  441. submitSelector: ".wym_submit",
  442. cancelSelector: ".wym_cancel",
  443. previewSelector: "",
  444. dialogTypeSelector: ".wym_dialog_type",
  445. dialogLinkSelector: ".wym_dialog_link",
  446. dialogImageSelector: ".wym_dialog_image",
  447. dialogTableSelector: ".wym_dialog_table",
  448. dialogPasteSelector: ".wym_dialog_paste",
  449. dialogPreviewSelector: ".wym_dialog_preview",
  450. updateSelector: ".wymupdate",
  451. updateEvent: "click",
  452. dialogFeatures: "menubar=no,titlebar=no,toolbar=no,resizable=no"
  453. + ",width=560,height=300,top=0,left=0",
  454. dialogFeaturesPreview: "menubar=no,titlebar=no,toolbar=no,resizable=no"
  455. + ",scrollbars=yes,width=560,height=300,top=0,left=0",
  456. dialogHtml: "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Strict//EN'"
  457. + " 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'>"
  458. + "<html dir='"
  459. + WYMeditor.DIRECTION
  460. + "'><head>"
  461. + "<link rel='stylesheet' type='text/css' media='screen'"
  462. + " href='"
  463. + WYMeditor.CSS_PATH
  464. + "' />"
  465. + "<title>"
  466. + WYMeditor.DIALOG_TITLE
  467. + "</title>"
  468. + "<script type='text/javascript'"
  469. + " src='"
  470. + WYMeditor.JQUERY_PATH
  471. + "'></script>"
  472. + "<script type='text/javascript'"
  473. + " src='"
  474. + WYMeditor.WYM_PATH
  475. + "'></script>"
  476. + "</head>"
  477. + WYMeditor.DIALOG_BODY
  478. + "</html>",
  479. dialogLinkHtml: "<body class='wym_dialog wym_dialog_link'"
  480. + " onload='WYMeditor.INIT_DIALOG(" + WYMeditor.INDEX + ")'"
  481. + ">"
  482. + "<form>"
  483. + "<fieldset>"
  484. + "<input type='hidden' class='wym_dialog_type' value='"
  485. + WYMeditor.DIALOG_LINK
  486. + "' />"
  487. + "<legend>{Link}</legend>"
  488. + "<div class='row'>"
  489. + "<label>{URL}</label>"
  490. + "<input type='text' class='wym_href' value='' size='40' />"
  491. + "</div>"
  492. + "<div class='row'>"
  493. + "<label>{Title}</label>"
  494. + "<input type='text' class='wym_title' value='' size='40' />"
  495. + "</div>"
  496. + "<div class='row row-indent'>"
  497. + "<input class='wym_submit' type='button'"
  498. + " value='{Submit}' />"
  499. + "<input class='wym_cancel' type='button'"
  500. + "value='{Cancel}' />"
  501. + "</div>"
  502. + "</fieldset>"
  503. + "</form>"
  504. + "</body>",
  505. dialogImageHtml: "<body class='wym_dialog wym_dialog_image'"
  506. + " onload='WYMeditor.INIT_DIALOG(" + WYMeditor.INDEX + ")'"
  507. + ">"
  508. + "<form>"
  509. + "<fieldset>"
  510. + "<input type='hidden' class='wym_dialog_type' value='"
  511. + WYMeditor.DIALOG_IMAGE
  512. + "' />"
  513. + "<legend>{Image}</legend>"
  514. + "<div class='row'>"
  515. + "<label>{URL}</label>"
  516. + "<input type='text' class='wym_src' value='' size='40' />"
  517. + "</div>"
  518. + "<div class='row'>"
  519. + "<label>{Alternative_Text}</label>"
  520. + "<input type='text' class='wym_alt' value='' size='40' />"
  521. + "</div>"
  522. + "<div class='row'>"
  523. + "<label>{Title}</label>"
  524. + "<input type='text' class='wym_title' value='' size='40' />"
  525. + "</div>"
  526. + "<div class='row row-indent'>"
  527. + "<input class='wym_submit' type='button'"
  528. + " value='{Submit}' />"
  529. + "<input class='wym_cancel' type='button'"
  530. + "value='{Cancel}' />"
  531. + "</div>"
  532. + "</fieldset>"
  533. + "</form>"
  534. + "</body>",
  535. dialogTableHtml: "<body class='wym_dialog wym_dialog_table'"
  536. + " onload='WYMeditor.INIT_DIALOG(" + WYMeditor.INDEX + ")'"
  537. + ">"
  538. + "<form>"
  539. + "<fieldset>"
  540. + "<input type='hidden' class='wym_dialog_type' value='"
  541. + WYMeditor.DIALOG_TABLE
  542. + "' />"
  543. + "<legend>{Table}</legend>"
  544. + "<div class='row'>"
  545. + "<label>{Caption}</label>"
  546. + "<input type='text' class='wym_caption' value='' size='40' />"
  547. + "</div>"
  548. + "<div class='row'>"
  549. + "<label>{Summary}</label>"
  550. + "<input type='text' class='wym_summary' value='' size='40' />"
  551. + "</div>"
  552. + "<div class='row'>"
  553. + "<label>{Number_Of_Rows}</label>"
  554. + "<input type='text' class='wym_rows' value='3' size='3' />"
  555. + "</div>"
  556. + "<div class='row'>"
  557. + "<label>{Number_Of_Cols}</label>"
  558. + "<input type='text' class='wym_cols' value='2' size='3' />"
  559. + "</div>"
  560. + "<div class='row row-indent'>"
  561. + "<input class='wym_submit' type='button'"
  562. + " value='{Submit}' />"
  563. + "<input class='wym_cancel' type='button'"
  564. + "value='{Cancel}' />"
  565. + "</div>"
  566. + "</fieldset>"
  567. + "</form>"
  568. + "</body>",
  569. dialogPasteHtml: "<body class='wym_dialog wym_dialog_paste'"
  570. + " onload='WYMeditor.INIT_DIALOG(" + WYMeditor.INDEX + ")'"
  571. + ">"
  572. + "<form>"
  573. + "<input type='hidden' class='wym_dialog_type' value='"
  574. + WYMeditor.DIALOG_PASTE
  575. + "' />"
  576. + "<fieldset>"
  577. + "<legend>{Paste_From_Word}</legend>"
  578. + "<div class='row'>"
  579. + "<textarea class='wym_text' rows='10' cols='50'></textarea>"
  580. + "</div>"
  581. + "<div class='row'>"
  582. + "<input class='wym_submit' type='button'"
  583. + " value='{Submit}' />"
  584. + "<input class='wym_cancel' type='button'"
  585. + "value='{Cancel}' />"
  586. + "</div>"
  587. + "</fieldset>"
  588. + "</form>"
  589. + "</body>",
  590. dialogPreviewHtml: "<body class='wym_dialog wym_dialog_preview'"
  591. + " onload='WYMeditor.INIT_DIALOG(" + WYMeditor.INDEX + ")'"
  592. + "></body>",
  593. dialogStyles: [],
  594. stringDelimiterLeft: "{",
  595. stringDelimiterRight:"}",
  596. preInit: null,
  597. preBind: null,
  598. postInit: null,
  599. preInitDialog: null,
  600. postInitDialog: null
  601. }, options);
  602. return this.each(function() {
  603. new WYMeditor.editor(jQuery(this),options);
  604. });
  605. };
  606. /* @name extend
  607. * @description Returns the WYMeditor instance based on its index
  608. */
  609. jQuery.extend({
  610. wymeditors: function(i) {
  611. return (WYMeditor.INSTANCES[i]);
  612. }
  613. });
  614. /********** WYMeditor **********/
  615. /* @name Wymeditor
  616. * @description WYMeditor class
  617. */
  618. /* @name init
  619. * @description Initializes a WYMeditor instance
  620. */
  621. WYMeditor.editor.prototype.init = function() {
  622. //load subclass - browser specific
  623. //unsupported browsers: do nothing
  624. if (jQuery.browser.msie) {
  625. var WymClass = new WYMeditor.WymClassExplorer(this);
  626. }
  627. else if (jQuery.browser.mozilla) {
  628. var WymClass = new WYMeditor.WymClassMozilla(this);
  629. }
  630. else if (jQuery.browser.opera) {
  631. var WymClass = new WYMeditor.WymClassOpera(this);
  632. }
  633. else if (jQuery.browser.safari) {
  634. var WymClass = new WYMeditor.WymClassSafari(this);
  635. }
  636. if(WymClass) {
  637. if(jQuery.isFunction(this._options.preInit)) this._options.preInit(this);
  638. var SaxListener = new WYMeditor.XhtmlSaxListener();
  639. jQuery.extend(SaxListener, WymClass);
  640. this.parser = new WYMeditor.XhtmlParser(SaxListener);
  641. if(this._options.styles || this._options.stylesheet){
  642. this.configureEditorUsingRawCss();
  643. }
  644. this.helper = new WYMeditor.XmlHelper();
  645. //extend the Wymeditor object
  646. //don't use jQuery.extend since 1.1.4
  647. //jQuery.extend(this, WymClass);
  648. for (var prop in WymClass) { this[prop] = WymClass[prop]; }
  649. //load wymbox
  650. this._box = jQuery(this._element).hide().after(this._options.boxHtml).next().addClass('wym_box_' + this._index);
  651. //store the instance index in wymbox and element replaced by editor instance
  652. //but keep it compatible with jQuery < 1.2.3, see #122
  653. if( jQuery.isFunction( jQuery.fn.data ) ) {
  654. jQuery.data(this._box.get(0), WYMeditor.WYM_INDEX, this._index);
  655. jQuery.data(this._element.get(0), WYMeditor.WYM_INDEX, this._index);
  656. }
  657. var h = WYMeditor.Helper;
  658. //construct the iframe
  659. var iframeHtml = this._options.iframeHtml;
  660. iframeHtml = h.replaceAll(iframeHtml, WYMeditor.INDEX, this._index);
  661. iframeHtml = h.replaceAll(iframeHtml, WYMeditor.IFRAME_BASE_PATH, this._options.iframeBasePath);
  662. //construct wymbox
  663. var boxHtml = jQuery(this._box).html();
  664. boxHtml = h.replaceAll(boxHtml, WYMeditor.LOGO, this._options.logoHtml);
  665. boxHtml = h.replaceAll(boxHtml, WYMeditor.TOOLS, this._options.toolsHtml);
  666. boxHtml = h.replaceAll(boxHtml, WYMeditor.CONTAINERS,this._options.containersHtml);
  667. boxHtml = h.replaceAll(boxHtml, WYMeditor.CLASSES, this._options.classesHtml);
  668. boxHtml = h.replaceAll(boxHtml, WYMeditor.HTML, this._options.htmlHtml);
  669. boxHtml = h.replaceAll(boxHtml, WYMeditor.IFRAME, iframeHtml);
  670. boxHtml = h.replaceAll(boxHtml, WYMeditor.STATUS, this._options.statusHtml);
  671. //construct tools list
  672. var aTools = eval(this._options.toolsItems);
  673. var sTools = "";
  674. for(var i = 0; i < aTools.length; i++) {
  675. var oTool = aTools[i];
  676. if(oTool.name && oTool.title)
  677. var sTool = this._options.toolsItemHtml;
  678. var sTool = h.replaceAll(sTool, WYMeditor.TOOL_NAME, oTool.name);
  679. sTool = h.replaceAll(sTool, WYMeditor.TOOL_TITLE, this._options.stringDelimiterLeft
  680. + oTool.title
  681. + this._options.stringDelimiterRight);
  682. sTool = h.replaceAll(sTool, WYMeditor.TOOL_CLASS, oTool.css);
  683. sTools += sTool;
  684. }
  685. boxHtml = h.replaceAll(boxHtml, WYMeditor.TOOLS_ITEMS, sTools);
  686. //construct classes list
  687. var aClasses = eval(this._options.classesItems);
  688. var sClasses = "";
  689. for(var i = 0; i < aClasses.length; i++) {
  690. var oClass = aClasses[i];
  691. if(oClass.name && oClass.title)
  692. var sClass = this._options.classesItemHtml;
  693. sClass = h.replaceAll(sClass, WYMeditor.CLASS_NAME, oClass.name);
  694. sClass = h.replaceAll(sClass, WYMeditor.CLASS_TITLE, oClass.title);
  695. sClasses += sClass;
  696. }
  697. boxHtml = h.replaceAll(boxHtml, WYMeditor.CLASSES_ITEMS, sClasses);
  698. //construct containers list
  699. var aContainers = eval(this._options.containersItems);
  700. var sContainers = "";
  701. for(var i = 0; i < aContainers.length; i++) {
  702. var oContainer = aContainers[i];
  703. if(oContainer.name && oContainer.title)
  704. var sContainer = this._options.containersItemHtml;
  705. sContainer = h.replaceAll(sContainer, WYMeditor.CONTAINER_NAME, oContainer.name);
  706. sContainer = h.replaceAll(sContainer, WYMeditor.CONTAINER_TITLE,
  707. this._options.stringDelimiterLeft
  708. + oContainer.title
  709. + this._options.stringDelimiterRight);
  710. sContainer = h.replaceAll(sContainer, WYMeditor.CONTAINER_CLASS, oContainer.css);
  711. sContainers += sContainer;
  712. }
  713. boxHtml = h.replaceAll(boxHtml, WYMeditor.CONTAINERS_ITEMS, sContainers);
  714. //l10n
  715. boxHtml = this.replaceStrings(boxHtml);
  716. //load html in wymbox
  717. jQuery(this._box).html(boxHtml);
  718. //hide the html value
  719. jQuery(this._box).find(this._options.htmlSelector).hide();
  720. //enable the skin
  721. this.loadSkin();
  722. }
  723. };
  724. WYMeditor.editor.prototype.bindEvents = function() {
  725. //copy the instance
  726. var wym = this;
  727. //handle click event on tools buttons
  728. jQuery(this._box).find(this._options.toolSelector).click(function() {
  729. wym._iframe.contentWindow.focus(); //See #154
  730. wym.exec(jQuery(this).attr(WYMeditor.NAME));
  731. return(false);
  732. });
  733. //handle click event on containers buttons
  734. jQuery(this._box).find(this._options.containerSelector).click(function() {
  735. wym.container(jQuery(this).attr(WYMeditor.NAME));
  736. return(false);
  737. });
  738. //handle keyup event on html value: set the editor value
  739. //handle focus/blur events to check if the element has focus, see #147
  740. jQuery(this._box).find(this._options.htmlValSelector)
  741. .keyup(function() { jQuery(wym._doc.body).html(jQuery(this).val());})
  742. .focus(function() { jQuery(this).toggleClass('hasfocus'); })
  743. .blur(function() { jQuery(this).toggleClass('hasfocus'); });
  744. //handle click event on classes buttons
  745. jQuery(this._box).find(this._options.classSelector).click(function() {
  746. var aClasses = eval(wym._options.classesItems);
  747. var sName = jQuery(this).attr(WYMeditor.NAME);
  748. var oClass = WYMeditor.Helper.findByName(aClasses, sName);
  749. if(oClass) {
  750. var jqexpr = oClass.expr;
  751. wym.toggleClass(sName, jqexpr);
  752. }
  753. wym._iframe.contentWindow.focus(); //See #154
  754. return(false);
  755. });
  756. //handle event on update element
  757. jQuery(this._options.updateSelector)
  758. .bind(this._options.updateEvent, function() {
  759. wym.update();
  760. });
  761. };
  762. WYMeditor.editor.prototype.ready = function() {
  763. return(this._doc != null);
  764. };
  765. /********** METHODS **********/
  766. /* @name box
  767. * @description Returns the WYMeditor container
  768. */
  769. WYMeditor.editor.prototype.box = function() {
  770. return(this._box);
  771. };
  772. /* @name html
  773. * @description Get/Set the html value
  774. */
  775. WYMeditor.editor.prototype.html = function(html) {
  776. if(typeof html === 'string') jQuery(this._doc.body).html(html);
  777. else return(jQuery(this._doc.body).html());
  778. };
  779. /* @name xhtml
  780. * @description Cleans up the HTML
  781. */
  782. WYMeditor.editor.prototype.xhtml = function() {
  783. return this.parser.parse(this.html());
  784. };
  785. /* @name exec
  786. * @description Executes a button command
  787. */
  788. WYMeditor.editor.prototype.exec = function(cmd) {
  789. //base function for execCommand
  790. //open a dialog or exec
  791. switch(cmd) {
  792. case WYMeditor.CREATE_LINK:
  793. var container = this.container();
  794. if(container || this._selected_image) this.dialog(WYMeditor.DIALOG_LINK);
  795. break;
  796. case WYMeditor.INSERT_IMAGE:
  797. this.dialog(WYMeditor.DIALOG_IMAGE);
  798. break;
  799. case WYMeditor.INSERT_TABLE:
  800. this.dialog(WYMeditor.DIALOG_TABLE);
  801. break;
  802. case WYMeditor.PASTE:
  803. this.dialog(WYMeditor.DIALOG_PASTE);
  804. break;
  805. case WYMeditor.TOGGLE_HTML:
  806. this.update();
  807. this.toggleHtml();
  808. //partially fixes #121 when the user manually inserts an image
  809. if(!jQuery(this._box).find(this._options.htmlSelector).is(':visible'))
  810. this.listen();
  811. break;
  812. case WYMeditor.PREVIEW:
  813. this.dialog(WYMeditor.PREVIEW, this._options.dialogFeaturesPreview);
  814. break;
  815. default:
  816. this._exec(cmd);
  817. break;
  818. }
  819. };
  820. /* @name container
  821. * @description Get/Set the selected container
  822. */
  823. WYMeditor.editor.prototype.container = function(sType) {
  824. if(sType) {
  825. var container = null;
  826. if(sType.toLowerCase() == WYMeditor.TH) {
  827. container = this.container();
  828. //find the TD or TH container
  829. switch(container.tagName.toLowerCase()) {
  830. case WYMeditor.TD: case WYMeditor.TH:
  831. break;
  832. default:
  833. var aTypes = new Array(WYMeditor.TD,WYMeditor.TH);
  834. container = this.findUp(this.container(), aTypes);
  835. break;
  836. }
  837. //if it exists, switch
  838. if(container!=null) {
  839. sType = (container.tagName.toLowerCase() == WYMeditor.TD)? WYMeditor.TH: WYMeditor.TD;
  840. this.switchTo(container,sType);
  841. this.update();
  842. }
  843. } else {
  844. //set the container type
  845. var aTypes=new Array(WYMeditor.P,WYMeditor.H1,WYMeditor.H2,WYMeditor.H3,WYMeditor.H4,WYMeditor.H5,
  846. WYMeditor.H6,WYMeditor.PRE,WYMeditor.BLOCKQUOTE);
  847. container = this.findUp(this.container(), aTypes);
  848. if(container) {
  849. var newNode = null;
  850. //blockquotes must contain a block level element
  851. if(sType.toLowerCase() == WYMeditor.BLOCKQUOTE) {
  852. var blockquote = this.findUp(this.container(), WYMeditor.BLOCKQUOTE);
  853. if(blockquote == null) {
  854. newNode = this._doc.createElement(sType);
  855. container.parentNode.insertBefore(newNode,container);
  856. newNode.appendChild(container);
  857. this.setFocusToNode(newNode.firstChild);
  858. } else {
  859. var nodes = blockquote.childNodes;
  860. var lgt = nodes.length;
  861. var firstNode = null;
  862. if(lgt > 0) firstNode = nodes.item(0);
  863. for(var x=0; x<lgt; x++) {
  864. blockquote.parentNode.insertBefore(nodes.item(0),blockquote);
  865. }
  866. blockquote.parentNode.removeChild(blockquote);
  867. if(firstNode) this.setFocusToNode(firstNode);
  868. }
  869. }
  870. else this.switchTo(container,sType);
  871. this.update();
  872. }
  873. }
  874. }
  875. else return(this.selected());
  876. };
  877. /* @name toggleClass
  878. * @description Toggles class on selected element, or one of its parents
  879. */
  880. WYMeditor.editor.prototype.toggleClass = function(sClass, jqexpr) {
  881. var container = (this._selected_image
  882. ? this._selected_image
  883. : jQuery(this.selected()));
  884. container = jQuery(container).parentsOrSelf(jqexpr);
  885. jQuery(container).toggleClass(sClass);
  886. if(!jQuery(container).attr(WYMeditor.CLASS)) jQuery(container).removeAttr(this._class);
  887. };
  888. /* @name findUp
  889. * @description Returns the first parent or self container, based on its type
  890. */
  891. WYMeditor.editor.prototype.findUp = function(node, filter) {
  892. //filter is a string or an array of strings
  893. if(node) {
  894. var tagname = node.tagName.toLowerCase();
  895. if(typeof(filter) == WYMeditor.STRING) {
  896. while(tagname != filter && tagname != WYMeditor.BODY) {
  897. node = node.parentNode;
  898. tagname = node.tagName.toLowerCase();
  899. }
  900. } else {
  901. var bFound = false;
  902. while(!bFound && tagname != WYMeditor.BODY) {
  903. for(var i = 0; i < filter.length; i++) {
  904. if(tagname == filter[i]) {
  905. bFound = true;
  906. break;
  907. }
  908. }
  909. if(!bFound) {
  910. node = node.parentNode;
  911. tagname = node.tagName.toLowerCase();
  912. }
  913. }
  914. }
  915. if(tagname != WYMeditor.BODY) return(node);
  916. else return(null);
  917. } else return(null);
  918. };
  919. /* @name switchTo
  920. * @description Switch the node's type
  921. */
  922. WYMeditor.editor.prototype.switchTo = function(node,sType) {
  923. var newNode = this._doc.createElement(sType);
  924. var html = jQuery(node).html();
  925. node.parentNode.replaceChild(newNode,node);
  926. jQuery(newNode).html(html);
  927. this.setFocusToNode(newNode);
  928. };
  929. WYMeditor.editor.prototype.replaceStrings = function(sVal) {
  930. //check if the language file has already been loaded
  931. //if not, get it via a synchronous ajax call
  932. if(!WYMeditor.STRINGS[this._options.lang]) {
  933. try {
  934. eval(jQuery.ajax({url:this._options.langPath
  935. + this._options.lang + '.js', async:false}).responseText);
  936. } catch(e) {
  937. WYMeditor.console.error("WYMeditor: error while parsing language file.");
  938. return sVal;
  939. }
  940. }
  941. //replace all the strings in sVal and return it
  942. for (var key in WYMeditor.STRINGS[this._options.lang]) {
  943. sVal = WYMeditor.Helper.replaceAll(sVal, this._options.stringDelimiterLeft + key
  944. + this._options.stringDelimiterRight,
  945. WYMeditor.STRINGS[this._options.lang][key]);
  946. };
  947. return(sVal);
  948. };
  949. WYMeditor.editor.prototype.encloseString = function(sVal) {
  950. return(this._options.stringDelimiterLeft
  951. + sVal
  952. + this._options.stringDelimiterRight);
  953. };
  954. /* @name status
  955. * @description Prints a status message
  956. */
  957. WYMeditor.editor.prototype.status = function(sMessage) {
  958. //print status message
  959. jQuery(this._box).find(this._options.statusSelector).html(sMessage);
  960. };
  961. /* @name update
  962. * @description Updates the element and textarea values
  963. */
  964. WYMeditor.editor.prototype.update = function() {
  965. var html = this.xhtml();
  966. jQuery(this._element).val(html);
  967. jQuery(this._box).find(this._options.htmlValSelector).not('.hasfocus').val(html); //#147
  968. };
  969. /* @name dialog
  970. * @description Opens a dialog box
  971. */
  972. WYMeditor.editor.prototype.dialog = function( dialogType, dialogFeatures, bodyHtml ) {
  973. var features = dialogFeatures || this._wym._options.dialogFeatures;
  974. var wDialog = window.open('', 'dialog', features);
  975. if(wDialog) {
  976. var sBodyHtml = "";
  977. switch( dialogType ) {
  978. case(WYMeditor.DIALOG_LINK):
  979. sBodyHtml = this._options.dialogLinkHtml;
  980. break;
  981. case(WYMeditor.DIALOG_IMAGE):
  982. sBodyHtml = this._options.dialogImageHtml;
  983. break;
  984. case(WYMeditor.DIALOG_TABLE):
  985. sBodyHtml = this._options.dialogTableHtml;
  986. break;
  987. case(WYMeditor.DIALOG_PASTE):
  988. sBodyHtml = this._options.dialogPasteHtml;
  989. break;
  990. case(WYMeditor.PREVIEW):
  991. sBodyHtml = this._options.dialogPreviewHtml;
  992. break;
  993. default:
  994. sBodyHtml = bodyHtml;
  995. }
  996. var h = WYMeditor.Helper;
  997. //construct the dialog
  998. var dialogHtml = this._options.dialogHtml;
  999. dialogHtml = h.replaceAll(dialogHtml, WYMeditor.BASE_PATH, this._options.basePath);
  1000. dialogHtml = h.replaceAll(dialogHtml, WYMeditor.DIRECTION, this._options.direction);
  1001. dialogHtml = h.replaceAll(dialogHtml, WYMeditor.CSS_PATH, this._options.skinPath + WYMeditor.SKINS_DEFAULT_CSS);
  1002. dialogHtml = h.replaceAll(dialogHtml, WYMeditor.WYM_PATH, this._options.wymPath);
  1003. dialogHtml = h.replaceAll(dialogHtml, WYMeditor.JQUERY_PATH, this._options.jQueryPath);
  1004. dialogHtml = h.replaceAll(dialogHtml, WYMeditor.DIALOG_TITLE, this.encloseString( dialogType ));
  1005. dialogHtml = h.replaceAll(dialogHtml, WYMeditor.DIALOG_BODY, sBodyHtml);
  1006. dialogHtml = h.replaceAll(dialogHtml, WYMeditor.INDEX, this._index);
  1007. dialogHtml = this.replaceStrings(dialogHtml);
  1008. var doc = wDialog.document;
  1009. doc.write(dialogHtml);
  1010. doc.close();
  1011. }
  1012. };
  1013. /* @name toggleHtml
  1014. * @description Shows/Hides the HTML
  1015. */
  1016. WYMeditor.editor.prototype.toggleHtml = function() {
  1017. jQuery(this._box).find(this._options.htmlSelector).toggle();
  1018. };
  1019. WYMeditor.editor.prototype.uniqueStamp = function() {
  1020. var now = new Date();
  1021. return("wym-" + now.getTime());
  1022. };
  1023. WYMeditor.editor.prototype.paste = function(sData) {
  1024. var sTmp;
  1025. var container = this.selected();
  1026. //split the data, using double newlines as the separator
  1027. var aP = sData.split(this._newLine + this._newLine);
  1028. var rExp = new RegExp(this._newLine, "g");
  1029. //add a P for each item
  1030. if(container && container.tagName.toLowerCase() != WYMeditor.BODY) {
  1031. for(x = aP.length - 1; x >= 0; x--) {
  1032. sTmp = aP[x];
  1033. //simple newlines are replaced by a break
  1034. sTmp = sTmp.replace(rExp, "<br />");
  1035. jQuery(container).after("<p>" + sTmp + "</p>");
  1036. }
  1037. } else {
  1038. for(x = 0; x < aP.length; x++) {
  1039. sTmp = aP[x];
  1040. //simple newlines are replaced by a break
  1041. sTmp = sTmp.replace(rExp, "<br />");
  1042. jQuery(this._doc.body).append("<p>" + sTmp + "</p>");
  1043. }
  1044. }
  1045. };
  1046. WYMeditor.editor.prototype.insert = function(html) {
  1047. // Do we have a selection?
  1048. if (this._iframe.contentWindow.getSelection().focusNode != null) {
  1049. // Overwrite selection with provided html
  1050. this._exec( WYMeditor.INSERT_HTML, html);
  1051. } else {
  1052. // Fall back to the internal paste function if there's no selection
  1053. this.paste(html)
  1054. }
  1055. };
  1056. WYMeditor.editor.prototype.wrap = function(left, right) {
  1057. // Do we have a selection?
  1058. if (this._iframe.contentWindow.getSelection().focusNode != null) {
  1059. // Wrap selection with provided html
  1060. this._exec( WYMeditor.INSERT_HTML, left + this._iframe.contentWindow.getSelection().toString() + right);
  1061. }
  1062. };
  1063. WYMeditor.editor.prototype.unwrap = function() {
  1064. // Do we have a selection?
  1065. if (this._iframe.contentWindow.getSelection().focusNode != null) {
  1066. // Unwrap selection
  1067. this._exec( WYMeditor.INSERT_HTML, this._iframe.contentWindow.getSelection().toString() );
  1068. }
  1069. };
  1070. WYMeditor.editor.prototype.addCssRules = function(doc, aCss) {
  1071. var styles = doc.styleSheets[0];
  1072. if(styles) {
  1073. for(var i = 0; i < aCss.length; i++) {
  1074. var oCss = aCss[i];
  1075. if(oCss.name && oCss.css) this.addCssRule(styles, oCss);
  1076. }
  1077. }
  1078. };
  1079. /********** CONFIGURATION **********/
  1080. WYMeditor.editor.prototype.computeBasePath = function() {
  1081. return jQuery(jQuery.grep(jQuery('script'), function(s){
  1082. return (s.src && s.src.match(/jquery\.wymeditor(\.pack|\.min|\.packed)?\.js(\?.*)?$/ ))
  1083. })).attr('src').replace(/jquery\.wymeditor(\.pack|\.min|\.packed)?\.js(\?.*)?$/, '');
  1084. };
  1085. WYMeditor.editor.prototype.computeWymPath = function() {
  1086. return jQuery(jQuery.grep(jQuery('script'), function(s){
  1087. return (s.src && s.src.match(/jquery\.wymeditor(\.pack|\.min|\.packed)?\.js(\?.*)?$/ ))
  1088. })).attr('src');
  1089. };
  1090. WYMeditor.editor.prototype.computeJqueryPath = function() {
  1091. return jQuery(jQuery.grep(jQuery('script'), function(s){
  1092. return (s.src && s.src.match(/jquery(-(.*)){0,1}(\.pack|\.min|\.packed)?\.js(\?.*)?$/ ))
  1093. })).attr('src');
  1094. };
  1095. WYMeditor.editor.prototype.computeCssPath = function() {
  1096. return jQuery(jQuery.grep(jQuery('link'), function(s){
  1097. return (s.href && s.href.match(/wymeditor\/skins\/(.*)screen\.css(\?.*)?$/ ))
  1098. })).attr('href');
  1099. };
  1100. WYMeditor.editor.prototype.configureEditorUsingRawCss = function() {
  1101. var CssParser = new WYMeditor.WymCssParser();
  1102. if(this._options.stylesheet){
  1103. CssParser.parse(jQuery.ajax({url: this._options.stylesheet,async:false}).responseText);
  1104. }else{
  1105. CssParser.parse(this._options.styles, false);
  1106. }
  1107. if(this._options.classesItems.length == 0) {
  1108. this._options.classesItems = CssParser.css_settings.classesItems;
  1109. }
  1110. if(this._options.editorStyles.length == 0) {
  1111. this._options.editorStyles = CssParser.css_settings.editorStyles;
  1112. }
  1113. if(this._options.dialogStyles.length == 0) {
  1114. this._options.dialogStyles = CssParser.css_settings.dialogStyles;
  1115. }
  1116. };
  1117. /********** EVENTS **********/
  1118. WYMeditor.editor.prototype.listen = function() {
  1119. //don't use jQuery.find() on the iframe body
  1120. //because of MSIE + jQuery + expando issue (#JQ1143)
  1121. //jQuery(this._doc.body).find("*").bind("mouseup", this.mouseup);
  1122. jQuery(this._doc.body).bind("mousedown", this.mousedown);
  1123. var images = this._doc.body.getElementsByTagName("img");
  1124. for(var i=0; i < images.length; i++) {
  1125. jQuery(images[i]).bind("mousedown", this.mousedown);
  1126. }
  1127. };
  1128. WYMeditor.editor.prototype.mousedown = function(evt) {
  1129. var wym = WYMeditor.INSTANCES[this.ownerDocument.title];
  1130. wym._selected_image = (this.tagName.toLowerCase() == WYMeditor.IMG) ? this : null;
  1131. evt.stopPropagation();
  1132. };
  1133. /********** SKINS **********/
  1134. /*
  1135. * Function: WYMeditor.loadCss
  1136. * Loads a stylesheet in the document.
  1137. *
  1138. * Parameters:
  1139. * href - The CSS path.
  1140. */
  1141. WYMeditor.loadCss = function(href) {
  1142. var link = document.createElement('link');
  1143. link.rel = 'stylesheet';
  1144. link.href = href;
  1145. var head = jQuery('head').get(0);
  1146. head.appendChild(link);
  1147. };
  1148. /*
  1149. * Function: WYMeditor.editor.loadSkin
  1150. * Loads the skin CSS and initialization script (if needed).
  1151. */
  1152. WYMeditor.editor.prototype.loadSkin = function() {
  1153. //does the user want to automatically load the CSS (default: yes)?
  1154. //we also test if it hasn't been already loaded by another instance
  1155. //see below for a better (second) test
  1156. if(this._options.loadSkin && !WYMeditor.SKINS[this._options.skin]) {
  1157. //check if it hasn't been already loaded
  1158. //so we don't load it more than once
  1159. //(we check the existing <link> elements)
  1160. var found = false;
  1161. var rExp = new RegExp(this._options.skin
  1162. + '\/' + WYMeditor.SKINS_DEFAULT_CSS + '$');
  1163. jQuery('link').each( function() {
  1164. if(this.href.match(rExp)) found = true;
  1165. });
  1166. //load it, using the skin path
  1167. if(!found) WYMeditor.loadCss( this._options.skinPath
  1168. + WYMeditor.SKINS_DEFAULT_CSS );
  1169. }
  1170. //put the classname (ex. wym_skin_default) on wym_box
  1171. jQuery(this._box).addClass( "wym_skin_" + this._options.skin );
  1172. //does the user want to use some JS to initialize the skin (default: yes)?
  1173. //also check if it hasn't already been loaded by another instance
  1174. if(this._options.initSkin && !WYMeditor.SKINS[this._options.skin]) {
  1175. eval(jQuery.ajax({url:this._options.skinPath
  1176. + WYMeditor.SKINS_DEFAULT_JS, async:false}).responseText);
  1177. }
  1178. //init the skin, if needed
  1179. if(WYMeditor.SKINS[this._options.skin]
  1180. && WYMeditor.SKINS[this._options.skin].init)
  1181. WYMeditor.SKINS[this._options.skin].init(this);
  1182. };
  1183. /********** DIALOGS **********/
  1184. WYMeditor.INIT_DIALOG = function(index) {
  1185. var wym = window.opener.WYMeditor.INSTANCES[index];
  1186. var doc = window.document;
  1187. var selected = wym.selected();
  1188. var dialogType = jQuery(wym._options.dialogTypeSelector).val();
  1189. var sStamp = wym.uniqueStamp();
  1190. switch(dialogType) {
  1191. case WYMeditor.DIALOG_LINK:
  1192. //ensure that we select the link to populate the fields
  1193. if(selected && selected.tagName && selected.tagName.toLowerCase != WYMeditor.A)
  1194. selected = jQuery(selected).parentsOrSelf(WYMeditor.A);
  1195. //fix MSIE selection if link image has been clicked
  1196. if(!selected && wym._selected_image)
  1197. selected = jQuery(wym._selected_image).parentsOrSelf(WYMeditor.A);
  1198. break;
  1199. }
  1200. //pre-init functions
  1201. if(jQuery.isFunction(wym._options.preInitDialog))
  1202. wym._options.preInitDialog(wym,window);
  1203. //add css rules from options
  1204. var styles = doc.styleSheets[0];
  1205. var aCss = eval(wym._options.dialogStyles);
  1206. wym.addCssRules(doc, aCss);
  1207. //auto populate fields if selected container (e.g. A)
  1208. if(selected) {
  1209. jQuery(wym._options.hrefSelector).val(jQuery(selected).attr(WYMeditor.HREF));
  1210. jQuery(wym._options.srcSelector).val(jQuery(selected).attr(WYMeditor.SRC));
  1211. jQuery(wym._options.titleSelector).val(jQuery(selected).attr(WYMeditor.TITLE));
  1212. jQuery(wym._options.altSelector).val(jQuery(selected).attr(WYMeditor.ALT));
  1213. }
  1214. //auto populate image fields if selected image
  1215. if(wym._selected_image) {
  1216. jQuery(wym._options.dialogImageSelector + " " + wym._options.srcSelector)
  1217. .val(jQuery(wym._selected_image).attr(WYMeditor.SRC));
  1218. jQuery(wym._options.dialogImageSelector + " " + wym._options.titleSelector)
  1219. .val(jQuery(wym._selected_image).attr(WYMeditor.TITLE));
  1220. jQuery(wym._options.dialogImageSelector + " " + wym._options.altSelector)
  1221. .val(jQuery(wym._selected_image).attr(WYMeditor.ALT));
  1222. }
  1223. jQuery(wym._options.dialogLinkSelector + " "
  1224. + wym._options.submitSelector).click(function() {
  1225. var sUrl = jQuery(wym._options.hrefSelector).val();
  1226. if(sUrl.length > 0) {
  1227. wym._exec(WYMeditor.CREATE_LINK, sStamp);
  1228. jQuery("a[href=" + sStamp + "]", wym._doc.body)
  1229. .attr(WYMeditor.HREF, sUrl)
  1230. .attr(WYMeditor.TITLE, jQuery(wym._options.titleSelector).val());
  1231. }
  1232. window.close();
  1233. });
  1234. jQuery(wym._options.dialogImageSelector + " "
  1235. + wym._options.submitSelector).click(function() {
  1236. var sUrl = jQuery(wym._options.srcSelector).val();
  1237. if(sUrl.length > 0) {
  1238. wym._exec(WYMeditor.INSERT_IMAGE, sStamp);
  1239. jQuery("img[src$=" + sStamp + "]", wym._doc.body)
  1240. .attr(WYMeditor.SRC, sUrl)
  1241. .attr(WYMeditor.TITLE, jQuery(wym._options.titleSelector).val())
  1242. .attr(WYMeditor.ALT, jQuery(wym._options.altSelector).val());
  1243. }
  1244. window.close();
  1245. });
  1246. jQuery(wym._options.dialogTableSelector + " "
  1247. + wym._options.submitSelector).click(function() {
  1248. var iRows = jQuery(wym._options.rowsSelector).val();
  1249. var iCols = jQuery(wym._options.colsSelector).val();
  1250. if(iRows > 0 && iCols > 0) {
  1251. var table = wym._doc.createElement(WYMeditor.TABLE);
  1252. var newRow = null;
  1253. var newCol = null;
  1254. var sCaption = jQuery(wym._options.captionSelector).val();
  1255. //we create the caption
  1256. var newCaption = table.createCaption();
  1257. newCaption.innerHTML = sCaption;
  1258. //we create the rows and cells
  1259. for(x=0; x<iRows; x++) {
  1260. newRow = table.insertRow(x);
  1261. for(y=0; y<iCols; y++) {newRow.insertCell(y);}
  1262. }
  1263. //set the summary attr
  1264. jQuery(table).attr('summary',
  1265. jQuery(wym._options.summarySelector).val());
  1266. //append the table after the selected container
  1267. var node = jQuery(wym.findUp(wym.container(),
  1268. WYMeditor.MAIN_CONTAINERS)).get(0);
  1269. if(!node || !node.parentNode) jQuery(wym._doc.body).append(table);
  1270. else jQuery(node).after(table);
  1271. }
  1272. window.close();
  1273. });
  1274. jQuery(wym._options.dialogPasteSelector + " "
  1275. + wym._options.submitSelector).click(function() {
  1276. var sText = jQuery(wym._options.textSelector).val();
  1277. wym.paste(sText);
  1278. window.close();
  1279. });
  1280. jQuery(wym._options.dialogPreviewSelector + " "
  1281. + wym._options.previewSelector)
  1282. .html(wym.xhtml());
  1283. //cancel button
  1284. jQuery(wym._options.cancelSelector).mousedown(function() {
  1285. window.close();
  1286. });
  1287. //pre-init functions
  1288. if(jQuery.isFunction(wym._options.postInitDialog))
  1289. wym._options.postInitDialog(wym,window);
  1290. };
  1291. /********** XHTML LEXER/PARSER **********/
  1292. /*
  1293. * @name xml
  1294. * @description Use these methods to generate XML and XHTML compliant tags and
  1295. * escape tag attributes correctly
  1296. * @author Bermi Ferrer - http://bermi.org
  1297. * @author David Heinemeier Hansson http://loudthinking.com
  1298. */
  1299. WYMeditor.XmlHelper = function()
  1300. {
  1301. this._entitiesDiv = document.createElement('div');
  1302. return this;
  1303. };
  1304. /*
  1305. * @name tag
  1306. * @description
  1307. * Returns an empty HTML tag of type *name* which by default is XHTML
  1308. * compliant. Setting *open* to true will create an open tag compatible
  1309. * with HTML 4.0 and below. Add HTML attributes by passing an attributes
  1310. * array to *options*. For attributes with no value like (disabled and
  1311. * readonly), give it a value of true in the *options* array.
  1312. *
  1313. * Examples:
  1314. *
  1315. * this.tag('br')
  1316. * # => <br />
  1317. * this.tag ('br', false, true)
  1318. * # => <br>
  1319. * this.tag ('input', jQuery({type:'text',disabled:true }) )
  1320. * # => <input type="text" disabled="disabled" />
  1321. */
  1322. WYMeditor.XmlHelper.prototype.tag = function(name, options, open)
  1323. {
  1324. options = options || false;
  1325. open = open || false;
  1326. return '<'+name+(options ? this.tagOptions(options) : '')+(open ? '>' : ' />');
  1327. };
  1328. /*
  1329. * @name contentTag
  1330. * @description
  1331. * Returns a XML block tag of type *name* surrounding the *content*. Add
  1332. * XML attributes by passing an attributes array to *options*. For attributes
  1333. * with no value like (disabled and readonly), give it a value of true in
  1334. * the *options* array. You can use symbols or strings for the attribute names.
  1335. *
  1336. * this.contentTag ('p', 'Hello world!' )
  1337. * # => <p>Hello world!</p>
  1338. * this.contentTag('div', this.contentTag('p', "Hello world!"), jQuery({class : "strong"}))
  1339. * # => <div class="strong"><p>Hello world!</p></div>
  1340. * this.contentTag("select", options, jQuery({multiple : true}))
  1341. * # => <select multiple="multiple">...options...</select>
  1342. */
  1343. WYMeditor.XmlHelper.prototype.contentTag = function(name, content, options)
  1344. {
  1345. options = options || false;
  1346. return '<'+name+(options ? this.tagOptions(options) : '')+'>'+content+'</'+name+'>';
  1347. };
  1348. /*
  1349. * @name cdataSection
  1350. * @description
  1351. * Returns a CDATA section for the given +content+. CDATA sections
  1352. * are used to escape blocks of text containing characters which would
  1353. * otherwise be recognized as markup. CDATA sections begin with the string
  1354. * <tt>&lt;![CDATA[</tt> and } with (and may not contain) the string
  1355. * <tt>]]></tt>.
  1356. */
  1357. WYMeditor.XmlHelper.prototype.cdataSection = function(content)
  1358. {
  1359. return '<![CDATA['+content+']]>';
  1360. };
  1361. /*
  1362. * @name escapeOnce
  1363. * @description
  1364. * Returns the escaped +xml+ without affecting existing escaped entities.
  1365. *
  1366. * this.escapeOnce( "1 > 2 &amp; 3")
  1367. * # => "1 &gt; 2 &amp; 3"
  1368. */
  1369. WYMeditor.XmlHelper.prototype.escapeOnce = function(xml)
  1370. {
  1371. return this._fixDoubleEscape(this.escapeEntities(xml));
  1372. };
  1373. /*
  1374. * @name _fixDoubleEscape
  1375. * @description
  1376. * Fix double-escaped entities, such as &amp;amp;, &amp;#123;, etc.
  1377. */
  1378. WYMeditor.XmlHelper.prototype._fixDoubleEscape = function(escaped)
  1379. {
  1380. return escaped.replace(/&amp;([a-z]+|(#\d+));/ig, "&$1;");
  1381. };
  1382. /*
  1383. * @name tagOptions
  1384. * @description
  1385. * Takes an array like the one generated by Tag.parseAttributes
  1386. * [["src", "http://www.editam.com/?a=b&c=d&amp;f=g"], ["title", "Editam, <Simplified> CMS"]]
  1387. * or an object like {src:"http://www.editam.com/?a=b&c=d&amp;f=g", title:"Editam, <Simplified> CMS"}
  1388. * and returns a string properly escaped like
  1389. * ' src = "http://www.editam.com/?a=b&amp;c=d&amp;f=g" title = "Editam, &lt;Simplified&gt; CMS"'
  1390. * which is valid for strict XHTML
  1391. */
  1392. WYMeditor.XmlHelper.prototype.tagOptions = function(options)
  1393. {
  1394. var xml = this;
  1395. xml._formated_options = '';
  1396. for (var key in options) {
  1397. var formated_options = '';
  1398. var value = options[key];
  1399. if(typeof value != 'function' && value.length > 0) {
  1400. if(parseInt(key) == key && typeof value == 'object'){
  1401. key = value.shift();
  1402. value = value.pop();
  1403. }
  1404. if(key != '' && value != ''){
  1405. xml._formated_options += ' '+key+'="'+xml.escapeOnce(value)+'"';
  1406. }
  1407. }
  1408. }
  1409. return xml._formated_options;
  1410. };
  1411. /*
  1412. * @name escapeEntities
  1413. * @description
  1414. * Escapes XML/HTML entities <, >, & and ". If seccond parameter is set to false it
  1415. * will not escape ". If set to true it will also escape '
  1416. */
  1417. WYMeditor.XmlHelper.prototype.escapeEntities = function(string, escape_quotes)
  1418. {
  1419. this._entitiesDiv.innerHTML = string;
  1420. this._entitiesDiv.textContent = string;
  1421. var result = this._entitiesDiv.innerHTML;
  1422. if(typeof escape_quotes == 'undefined'){
  1423. if(escape_quotes != false) result = result.replace('"', '&quot;');
  1424. if(escape_quotes == true) result = result.replace('"', '&#039;');
  1425. }
  1426. return result;
  1427. };
  1428. /*
  1429. * Parses a string conatining tag attributes and values an returns an array formated like
  1430. * [["src", "http://www.editam.com"], ["title", "Editam, Simplified CMS"]]
  1431. */
  1432. WYMeditor.XmlHelper.prototype.parseAttributes = function(tag_attributes)
  1433. {
  1434. // Use a compounded regex to match single quoted, double quoted and unquoted attribute pairs
  1435. var result = [];
  1436. var matches = tag_attributes.split(/((=\s*")(")("))|((=\s*\')(\')(\'))|((=\s*[^>\s]*))/g);
  1437. if(matches.toString() != tag_attributes){
  1438. for (var k in matches) {
  1439. var v = matches[k];
  1440. if(typeof v != 'function' && v.length != 0){
  1441. var re = new RegExp('(\\w+)\\s*'+v);
  1442. if(match = tag_attributes.match(re) ){
  1443. var value = v.replace(/^[\s=]+/, "");
  1444. var delimiter = value.charAt(0);
  1445. delimiter = delimiter == '"' ? '"' : (delimiter=="'"?"'":'');
  1446. if(delimiter != ''){
  1447. value = delimiter == '"' ? value.replace(/^"|"+$/g, '') : value.replace(/^'|'+$/g, '');
  1448. }
  1449. tag_attributes = tag_attributes.replace(match[0],'');
  1450. result.push([match[1] , value]);
  1451. }
  1452. }
  1453. }
  1454. }
  1455. return result;
  1456. };
  1457. /**
  1458. * XhtmlValidator for validating tag attributes
  1459. *
  1460. * @author Bermi Ferrer - http://bermi.org
  1461. */
  1462. WYMeditor.XhtmlValidator = {
  1463. "_attributes":
  1464. {
  1465. "core":
  1466. {
  1467. "except":[
  1468. "base",
  1469. "head",
  1470. "html",
  1471. "meta",
  1472. "param",
  1473. "script",
  1474. "style",
  1475. "title"
  1476. ],
  1477. "attributes":[
  1478. "class",
  1479. "id",
  1480. "style",
  1481. "title",
  1482. "accesskey",
  1483. "tabindex"
  1484. ]
  1485. },
  1486. "language":
  1487. {
  1488. "except":[
  1489. "base",
  1490. "br",
  1491. "hr",
  1492. "iframe",
  1493. "param",
  1494. "script"
  1495. ],
  1496. "attributes":
  1497. {
  1498. "dir":[
  1499. "ltr",
  1500. "rtl"
  1501. ],
  1502. "0":"lang",
  1503. "1":"xml:lang"
  1504. }
  1505. },
  1506. "keyboard":
  1507. {
  1508. "attributes":
  1509. {
  1510. "accesskey":/^(\w){1}$/,
  1511. "tabindex":/^(\d)+$/
  1512. }
  1513. }
  1514. },
  1515. "_events":
  1516. {
  1517. "window":
  1518. {
  1519. "only":[
  1520. "body"
  1521. ],
  1522. "attributes":[
  1523. "onload",
  1524. "onunload"
  1525. ]
  1526. },
  1527. "form":
  1528. {
  1529. "only":[
  1530. "form",
  1531. "input",
  1532. "textarea",
  1533. "select",
  1534. "a",
  1535. "label",
  1536. "button"
  1537. ],
  1538. "attributes":[
  1539. "onchange",
  1540. "onsubmit",
  1541. "onreset",
  1542. "onselect",
  1543. "onblur",
  1544. "onfocus"
  1545. ]
  1546. },
  1547. "keyboard":
  1548. {
  1549. "except":[
  1550. "base",
  1551. "bdo",
  1552. "br",
  1553. "frame",
  1554. "frameset",
  1555. "head",
  1556. "html",
  1557. "iframe",
  1558. "meta",
  1559. "param",
  1560. "script",
  1561. "style",
  1562. "title"
  1563. ],
  1564. "attributes":[
  1565. "onkeydown",
  1566. "onkeypress",
  1567. "onkeyup"
  1568. ]
  1569. },
  1570. "mouse":
  1571. {
  1572. "except":[
  1573. "base",
  1574. "bdo",
  1575. "br",
  1576. "head",
  1577. "html",
  1578. "meta",
  1579. "param",
  1580. "script",
  1581. "style",
  1582. "title"
  1583. ],
  1584. "attributes":[
  1585. "onclick",
  1586. "ondblclick",
  1587. "onmousedown",
  1588. "onmousemove",
  1589. "onmouseover",
  1590. "onmouseout",
  1591. "onmouseup"
  1592. ]
  1593. }
  1594. },
  1595. "_tags":
  1596. {
  1597. "a":
  1598. {
  1599. "attributes":
  1600. {
  1601. "0":"charset",
  1602. "1":"coords",
  1603. "2":"href",
  1604. "3":"hreflang",
  1605. "4":"name",
  1606. "rel":/^(alternate|designates|stylesheet|start|next|prev|contents|index|glossary|copyright|chapter|section|subsection|appendix|help|bookmark| |shortcut|icon)+$/,
  1607. "rev":/^(alternate|designates|stylesheet|start|next|prev|contents|index|glossary|copyright|chapter|section|subsection|appendix|help|bookmark| |shortcut|icon)+$/,
  1608. "shape":/^(rect|rectangle|circ|circle|poly|polygon)$/,
  1609. "5":"type"
  1610. }
  1611. },
  1612. "0":"abbr",
  1613. "1":"acronym",
  1614. "2":"address",
  1615. "area":
  1616. {
  1617. "attributes":
  1618. {
  1619. "0":"alt",
  1620. "1":"coords",
  1621. "2":"href",
  1622. "nohref":/^(true|false)$/,
  1623. "shape":/^(rect|rectangle|circ|circle|poly|polygon)$/
  1624. },
  1625. "required":[
  1626. "alt"
  1627. ]
  1628. },
  1629. "3":"b",
  1630. "base":
  1631. {
  1632. "attributes":[
  1633. "href"
  1634. ],
  1635. "required":[
  1636. "href"
  1637. ]
  1638. },
  1639. "bdo":
  1640. {
  1641. "attributes":
  1642. {
  1643. "dir":/^(ltr|rtl)$/
  1644. },
  1645. "required":[
  1646. "dir"
  1647. ]
  1648. },
  1649. "4":"big",
  1650. "blockquote":
  1651. {
  1652. "attributes":[
  1653. "cite"
  1654. ]
  1655. },
  1656. "5":"body",
  1657. "6":"br",
  1658. "button":
  1659. {
  1660. "attributes":
  1661. {
  1662. "disabled":/^(disabled)$/,
  1663. "type":/^(button|reset|submit)$/,
  1664. "0":"value"
  1665. },
  1666. "inside":"form"
  1667. },
  1668. "7":"caption",
  1669. "8":"cite",
  1670. "9":"code",
  1671. "col":
  1672. {
  1673. "attributes":
  1674. {
  1675. "align":/^(right|left|center|justify)$/,
  1676. "0":"char",
  1677. "1":"charoff",
  1678. "span":/^(\d)+$/,
  1679. "valign":/^(top|middle|bottom|baseline)$/,
  1680. "2":"width"
  1681. },
  1682. "inside":"colgroup"
  1683. },
  1684. "colgroup":
  1685. {
  1686. "attributes":
  1687. {
  1688. "align":/^(right|left|center|justify)$/,
  1689. "0":"char",
  1690. "1":"charoff",
  1691. "span":/^(\d)+$/,
  1692. "valign":/^(top|middle|bottom|baseline)$/,
  1693. "2":"width"
  1694. }
  1695. },
  1696. "10":"dd",
  1697. "del":
  1698. {
  1699. "attributes":
  1700. {
  1701. "0":"cite",
  1702. "datetime":/^([0-9]){8}/
  1703. }
  1704. },
  1705. "11":"div",
  1706. "12":"dfn",
  1707. "13":"dl",
  1708. "14":"dt",
  1709. "15":"em",
  1710. "fieldset":
  1711. {
  1712. "inside":"form"
  1713. },
  1714. "form":
  1715. {
  1716. "attributes":
  1717. {
  1718. "0":"action",
  1719. "1":"accept",
  1720. "2":"accept-charset",
  1721. "3":"enctype",
  1722. "method":/^(get|post)$/
  1723. },
  1724. "required":[
  1725. "action"
  1726. ]
  1727. },
  1728. "head":
  1729. {
  1730. "attributes":[
  1731. "profile"
  1732. ]
  1733. },
  1734. "16":"h1",
  1735. "17":"h2",
  1736. "18":"h3",
  1737. "19":"h4",
  1738. "20":"h5",
  1739. "21":"h6",
  1740. "22":"hr",
  1741. "html":
  1742. {
  1743. "attributes":[
  1744. "xmlns"
  1745. ]
  1746. },
  1747. "23":"i",
  1748. "img":
  1749. {
  1750. "attributes":[
  1751. "alt",
  1752. "src",
  1753. "height",
  1754. "ismap",
  1755. "longdesc",
  1756. "usemap",
  1757. "width"
  1758. ],
  1759. "required":[
  1760. "alt",
  1761. "src"
  1762. ]
  1763. },
  1764. "input":
  1765. {
  1766. "attributes":
  1767. {
  1768. "0":"accept",
  1769. "1":"alt",
  1770. "checked":/^(checked)$/,
  1771. "disabled":/^(disabled)$/,
  1772. "maxlength":/^(\d)+$/,
  1773. "2":"name",
  1774. "readonly":/^(readonly)$/,
  1775. "size":/^(\d)+$/,
  1776. "3":"src",
  1777. "type":/^(button|checkbox|file|hidden|image|password|radio|reset|submit|text)$/,
  1778. "4":"value"
  1779. },
  1780. "inside":"form"
  1781. },
  1782. "ins":
  1783. {
  1784. "attributes":
  1785. {
  1786. "0":"cite",
  1787. "datetime":/^([0-9]){8}/
  1788. }
  1789. },
  1790. "24":"kbd",
  1791. "label":
  1792. {
  1793. "attributes":[
  1794. "for"
  1795. ],
  1796. "inside":"form"
  1797. },
  1798. "25":"legend",
  1799. "26":"li",
  1800. "link":
  1801. {
  1802. "attributes":
  1803. {
  1804. "0":"charset",
  1805. "1":"href",
  1806. "2":"hreflang",
  1807. "media":/^(all|braille|print|projection|screen|speech|,|;| )+$/i,
  1808. //next comment line required by Opera!
  1809. /*"rel":/^(alternate|appendix|bookmark|chapter|contents|copyright|glossary|help|home|index|next|prev|section|start|stylesheet|subsection| |shortcut|icon)+$/i,*/
  1810. "rel":/^(alternate|appendix|bookmark|chapter|contents|copyright|glossary|help|home|index|next|prev|section|start|stylesheet|subsection| |shortcut|icon)+$/i,
  1811. "rev":/^(alternate|appendix|boo