ckeditor.asp 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955
  1. <%
  2. '
  3. ' Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
  4. ' For licensing, see LICENSE.html or http://ckeditor.com/license
  5. ' Shared variable for all instances ("static")
  6. dim CKEDITOR_initComplete
  7. dim CKEDITOR_returnedEvents
  8. ''
  9. ' \brief CKEditor class that can be used to create editor
  10. ' instances in ASP pages on server side.
  11. ' @see http://ckeditor.com
  12. '
  13. ' Sample usage:
  14. ' @code
  15. ' editor = new CKEditor
  16. ' editor.editor "editor1", "<p>Initial value.</p>", empty, empty
  17. ' @endcode
  18. Class CKEditor
  19. ''
  20. ' The version of %CKEditor.
  21. private version
  22. ''
  23. ' A constant string unique for each release of %CKEditor.
  24. private mTimeStamp
  25. ''
  26. ' URL to the %CKEditor installation directory (absolute or relative to document root).
  27. ' If not set, CKEditor will try to guess it's path.
  28. '
  29. ' Example usage:
  30. ' @code
  31. ' editor.basePath = "/ckeditor/"
  32. ' @endcode
  33. Public basePath
  34. ''
  35. ' A boolean variable indicating whether CKEditor has been initialized.
  36. ' Set it to true only if you have already included
  37. ' &lt;script&gt; tag loading ckeditor.js in your website.
  38. Public initialized
  39. ''
  40. ' Boolean variable indicating whether created code should be printed out or returned by a function.
  41. '
  42. ' Example 1: get the code creating %CKEditor instance and print it on a page with the "echo" function.
  43. ' @code
  44. ' editor = new CKEditor
  45. ' editor.returnOutput = true
  46. ' code = editor.editor("editor1", "<p>Initial value.</p>", empty, empty)
  47. ' response.write "<p>Editor 1:</p>"
  48. ' response.write code
  49. ' @endcode
  50. Public returnOutput
  51. ''
  52. ' A Dictionary with textarea attributes.
  53. '
  54. ' When %CKEditor is created with the editor() method, a HTML &lt;textarea&gt; element is created,
  55. ' it will be displayed to anyone with JavaScript disabled or with incompatible browser.
  56. public textareaAttributes
  57. ''
  58. ' A string indicating the creation date of %CKEditor.
  59. ' Do not change it unless you want to force browsers to not use previously cached version of %CKEditor.
  60. public timestamp
  61. ''
  62. ' A dictionary that holds the instance configuration.
  63. private oInstanceConfig
  64. ''
  65. ' A dictionary that holds the configuration for all the instances.
  66. private oAllInstancesConfig
  67. ''
  68. ' A dictionary that holds event listeners for the instance.
  69. private oInstanceEvents
  70. ''
  71. ' A dictionary that holds event listeners for all the instances.
  72. private oAllInstancesEvents
  73. ''
  74. ' A Dictionary that holds global event listeners (CKEDITOR object)
  75. private oGlobalEvents
  76. Private Sub Class_Initialize()
  77. version = "3.6.2"
  78. timeStamp = "B8DJ5M3"
  79. mTimeStamp = "B8DJ5M3"
  80. Set oInstanceConfig = CreateObject("Scripting.Dictionary")
  81. Set oAllInstancesConfig = CreateObject("Scripting.Dictionary")
  82. Set oInstanceEvents = CreateObject("Scripting.Dictionary")
  83. Set oAllInstancesEvents = CreateObject("Scripting.Dictionary")
  84. Set oGlobalEvents = CreateObject("Scripting.Dictionary")
  85. Set textareaAttributes = CreateObject("Scripting.Dictionary")
  86. textareaAttributes.Add "rows", 8
  87. textareaAttributes.Add "cols", 60
  88. End Sub
  89. ''
  90. ' Creates a %CKEditor instance.
  91. ' In incompatible browsers %CKEditor will downgrade to plain HTML &lt;textarea&gt; element.
  92. '
  93. ' @param name (string) Name of the %CKEditor instance (this will be also the "name" attribute of textarea element).
  94. ' @param value (string) Initial value.
  95. '
  96. ' Example usage:
  97. ' @code
  98. ' set editor = New CKEditor
  99. ' editor.editor "field1", "<p>Initial value.</p>"
  100. ' @endcode
  101. '
  102. ' Advanced example:
  103. ' @code
  104. ' set editor = new CKEditor
  105. ' set config = CreateObject("Scripting.Dictionary")
  106. ' config.Add "toolbar", Array( _
  107. ' Array( "Source", "-", "Bold", "Italic", "Underline", "Strike" ), _
  108. ' Array( "Image", "Link", "Unlink", "Anchor" ) _
  109. ' )
  110. ' set events = CreateObject("Scripting.Dictionary")
  111. ' events.Add "instanceReady", "function (evt) { alert('Loaded second editor: ' + evt.editor.name );}"
  112. ' editor.editor "field1", "<p>Initial value.</p>", config, events
  113. ' @endcode
  114. '
  115. public function editor(name, value)
  116. dim attr, out, js, customConfig, extraConfig
  117. dim attribute
  118. attr = ""
  119. for each attribute in textareaAttributes
  120. attr = attr & " " & attribute & "=""" & replace( textareaAttributes( attribute ), """", "&quot" ) & """"
  121. next
  122. out = "<textarea name=""" & name & """" & attr & ">" & Server.HtmlEncode(value) & "</textarea>" & vbcrlf
  123. if not(initialized) then
  124. out = out & init()
  125. end if
  126. set customConfig = configSettings()
  127. js = returnGlobalEvents()
  128. extraConfig = (new JSON)( empty, customConfig, false )
  129. if extraConfig<>"" then extraConfig = ", " & extraConfig
  130. js = js & "CKEDITOR.replace('" & name & "'" & extraConfig & ");"
  131. out = out & script(js)
  132. if not(returnOutput) then
  133. response.write out
  134. out = ""
  135. end if
  136. editor = out
  137. oInstanceConfig.RemoveAll
  138. oInstanceEvents.RemoveAll
  139. end function
  140. ''
  141. ' Replaces a &lt;textarea&gt; with a %CKEditor instance.
  142. '
  143. ' @param id (string) The id or name of textarea element.
  144. '
  145. ' Example 1: adding %CKEditor to &lt;textarea name="article"&gt;&lt;/textarea&gt; element:
  146. ' @code
  147. ' set editor = New CKEditor
  148. ' editor.replace "article"
  149. ' @endcode
  150. '
  151. public function replaceInstance(id)
  152. dim out, js, customConfig, extraConfig
  153. out = ""
  154. if not(initialized) then
  155. out = out & init()
  156. end if
  157. set customConfig = configSettings()
  158. js = returnGlobalEvents()
  159. extraConfig = (new JSON)( empty, customConfig, false )
  160. if extraConfig<>"" then extraConfig = ", " & extraConfig
  161. js = js & "CKEDITOR.replace('" & id & "'" & extraConfig & ");"
  162. out = out & script(js)
  163. if not(returnOutput) then
  164. response.write out
  165. out = ""
  166. end if
  167. replaceInstance = out
  168. oInstanceConfig.RemoveAll
  169. oInstanceEvents.RemoveAll
  170. end function
  171. ''
  172. ' Replace all &lt;textarea&gt; elements available in the document with editor instances.
  173. '
  174. ' @param className (string) If set, replace all textareas with class className in the page.
  175. '
  176. ' Example 1: replace all &lt;textarea&gt; elements in the page.
  177. ' @code
  178. ' editor = new CKEditor
  179. ' editor.replaceAll empty
  180. ' @endcode
  181. '
  182. ' Example 2: replace all &lt;textarea class="myClassName"&gt; elements in the page.
  183. ' @code
  184. ' editor = new CKEditor
  185. ' editor.replaceAll 'myClassName'
  186. ' @endcode
  187. '
  188. function replaceAll(className)
  189. dim out, js, customConfig
  190. out = ""
  191. if not(initialized) then
  192. out = out & init()
  193. end if
  194. set customConfig = configSettings()
  195. js = returnGlobalEvents()
  196. if (customConfig.Count=0) then
  197. if (isEmpty(className)) then
  198. js = js & "CKEDITOR.replaceAll();"
  199. else
  200. js = js & "CKEDITOR.replaceAll('" & className & "');"
  201. end if
  202. else
  203. js = js & "CKEDITOR.replaceAll( function(textarea, config) {\n"
  204. if not(isEmpty(className)) then
  205. js = js & " var classRegex = new RegExp('(?:^| )' + '" & className & "' + '(?:$| )');\n"
  206. js = js & " if (!classRegex.test(textarea.className))\n"
  207. js = js & " return false;\n"
  208. end if
  209. js = js & " CKEDITOR.tools.extend(config, " & (new JSON)( empty, customConfig, false ) & ", true);"
  210. js = js & "} );"
  211. end if
  212. out = out & script(js)
  213. if not(returnOutput) then
  214. response.write out
  215. out = ""
  216. end if
  217. replaceAll = out
  218. oInstanceConfig.RemoveAll
  219. oInstanceEvents.RemoveAll
  220. end function
  221. ''
  222. ' A Dictionary that holds the %CKEditor configuration for all instances
  223. ' For the list of available options, see http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.config.html
  224. '
  225. ' Example usage:
  226. ' @code
  227. ' editor.config("height") = 400
  228. ' // Use @@ at the beggining of a string to ouput it without surrounding quotes.
  229. ' editor.config("width") = "@@screen.width * 0.8"
  230. ' @endcode
  231. Public Property Let Config( configKey, configValue )
  232. oAllInstancesConfig.Add configKey, configValue
  233. End Property
  234. ''
  235. ' Configuration options for the next instance
  236. '
  237. Public Property Let instanceConfig( configKey, configValue )
  238. oInstanceConfig.Add configKey, configValue
  239. End Property
  240. ''
  241. ' Adds event listener.
  242. ' Events are fired by %CKEditor in various situations.
  243. '
  244. ' @param eventName (string) Event name.
  245. ' @param javascriptCode (string) Javascript anonymous function or function name.
  246. '
  247. ' Example usage:
  248. ' @code
  249. ' editor.addEventHandler "instanceReady", "function (ev) { " & _
  250. ' " alert('Loaded: ' + ev.editor.name); " & _
  251. ' "}"
  252. ' @endcode
  253. '
  254. public sub addEventHandler(eventName, javascriptCode)
  255. if not(oAllInstancesEvents.Exists( eventName ) ) then
  256. oAllInstancesEvents.Add eventName, Array()
  257. end if
  258. dim listeners, size
  259. listeners = oAllInstancesEvents( eventName )
  260. size = ubound(listeners) + 1
  261. redim preserve listeners(size)
  262. listeners(size) = javascriptCode
  263. oAllInstancesEvents( eventName ) = listeners
  264. ' '' Avoid duplicates. fixme...
  265. ' if (!in_array($javascriptCode, $this->_events[$event])) {
  266. ' $this->_events[$event][] = $javascriptCode;
  267. ' }
  268. end sub
  269. ''
  270. ' Clear registered event handlers.
  271. ' Note: this function will have no effect on already created editor instances.
  272. '
  273. ' @param eventName (string) Event name, if set to 'empty' all event handlers will be removed.
  274. '
  275. public sub clearEventHandlers( eventName )
  276. if not(isEmpty( eventName )) then
  277. oAllInstancesEvents.Remove eventName
  278. else
  279. oAllInstancesEvents.RemoveAll
  280. end if
  281. end sub
  282. ''
  283. ' Adds event listener only for the next instance.
  284. ' Events are fired by %CKEditor in various situations.
  285. '
  286. ' @param eventName (string) Event name.
  287. ' @param javascriptCode (string) Javascript anonymous function or function name.
  288. '
  289. ' Example usage:
  290. ' @code
  291. ' editor.addInstanceEventHandler "instanceReady", "function (ev) { " & _
  292. ' " alert('Loaded: ' + ev.editor.name); " & _
  293. ' "}"
  294. ' @endcode
  295. '
  296. public sub addInstanceEventHandler(eventName, javascriptCode)
  297. if not(oInstanceEvents.Exists( eventName ) ) then
  298. oInstanceEvents.Add eventName, Array()
  299. end if
  300. dim listeners, size
  301. listeners = oInstanceEvents( eventName )
  302. size = ubound(listeners) + 1
  303. redim preserve listeners(size)
  304. listeners(size) = javascriptCode
  305. oInstanceEvents( eventName ) = listeners
  306. ' '' Avoid duplicates. fixme...
  307. ' if (!in_array($javascriptCode, $this->_events[$event])) {
  308. ' $this->_events[$event][] = $javascriptCode;
  309. ' }
  310. end sub
  311. ''
  312. ' Clear registered event handlers.
  313. ' Note: this function will have no effect on already created editor instances.
  314. '
  315. ' @param eventName (string) Event name, if set to 'empty' all event handlers will be removed.
  316. '
  317. public sub clearInstanceEventHandlers( eventName )
  318. if not(isEmpty( eventName )) then
  319. oInstanceEvents.Remove eventName
  320. else
  321. oInstanceEvents.RemoveAll
  322. end if
  323. end sub
  324. ''
  325. ' Adds global event listener.
  326. '
  327. ' @param event (string) Event name.
  328. ' @param javascriptCode (string) Javascript anonymous function or function name.
  329. '
  330. ' Example usage:
  331. ' @code
  332. ' editor.addGlobalEventHandler "dialogDefinition", "function (ev) { " & _
  333. ' " alert('Loading dialog: ' + ev.data.name); " & _
  334. ' "}"
  335. ' @endcode
  336. '
  337. public sub addGlobalEventHandler( eventName, javascriptCode)
  338. if not(oGlobalEvents.Exists( eventName ) ) then
  339. oGlobalEvents.Add eventName, Array()
  340. end if
  341. dim listeners, size
  342. listeners = oGlobalEvents( eventName )
  343. size = ubound(listeners) + 1
  344. redim preserve listeners(size)
  345. listeners(size) = javascriptCode
  346. oGlobalEvents( eventName ) = listeners
  347. ' // Avoid duplicates.
  348. ' if (!in_array($javascriptCode, $this->_globalEvents[$event])) {
  349. ' $this->_globalEvents[$event][] = $javascriptCode;
  350. ' }
  351. end sub
  352. ''
  353. ' Clear registered global event handlers.
  354. ' Note: this function will have no effect if the event handler has been already printed/returned.
  355. '
  356. ' @param eventName (string) Event name, if set to 'empty' all event handlers will be removed .
  357. '
  358. public sub clearGlobalEventHandlers( eventName )
  359. if not(isEmpty( eventName )) then
  360. oGlobalEvents.Remove eventName
  361. else
  362. oGlobalEvents.RemoveAll
  363. end if
  364. end sub
  365. ''
  366. ' Prints javascript code.
  367. '
  368. ' @param string js
  369. '
  370. private function script(js)
  371. script = "<script type=""text/javascript"">" & _
  372. "//<![CDATA[" & vbcrlf & _
  373. js & vbcrlf & _
  374. "//]]>" & _
  375. "</script>" & vbcrlf
  376. end function
  377. ''
  378. ' Returns the configuration array (global and instance specific settings are merged into one array).
  379. '
  380. ' @param instanceConfig (Dictionary) The specific configurations to apply to editor instance.
  381. ' @param instanceEvents (Dictionary) Event listeners for editor instance.
  382. '
  383. private function configSettings()
  384. dim mergedConfig, mergedEvents
  385. set mergedConfig = cloneDictionary(oAllInstancesConfig)
  386. set mergedEvents = cloneDictionary(oAllInstancesEvents)
  387. if not(isEmpty(oInstanceConfig)) then
  388. set mergedConfig = mergeDictionary(mergedConfig, oInstanceConfig)
  389. end if
  390. if not(isEmpty(oInstanceEvents)) then
  391. for each eventName in oInstanceEvents
  392. code = oInstanceEvents( eventName )
  393. if not(mergedEvents.Exists( eventName)) then
  394. mergedEvents.Add eventName, code
  395. else
  396. dim listeners, size
  397. listeners = mergedEvents( eventName )
  398. size = ubound(listeners)
  399. if isArray( code ) then
  400. addedCount = ubound(code)
  401. redim preserve listeners( size + addedCount + 1 )
  402. for i = 0 to addedCount
  403. listeners(size + i + 1) = code (i)
  404. next
  405. else
  406. size = size + 1
  407. redim preserve listeners(size)
  408. listeners(size) = code
  409. end if
  410. mergedEvents( eventName ) = listeners
  411. end if
  412. next
  413. end if
  414. dim i, eventName, handlers, configON, ub, code
  415. if mergedEvents.Count>0 then
  416. if mergedConfig.Exists( "on" ) then
  417. set configON = mergedConfig.items( "on" )
  418. else
  419. set configON = CreateObject("Scripting.Dictionary")
  420. mergedConfig.Add "on", configOn
  421. end if
  422. for each eventName in mergedEvents
  423. handlers = mergedEvents( eventName )
  424. code = ""
  425. if isArray(handlers) then
  426. uB = ubound(handlers)
  427. if (uB = 0) then
  428. code = handlers(0)
  429. else
  430. code = "function (ev) {"
  431. for i=0 to uB
  432. code = code & "(" & handlers(i) & ")(ev);"
  433. next
  434. code = code & "}"
  435. end if
  436. else
  437. code = handlers
  438. end if
  439. ' Using @@ at the beggining to signal JSON that we don't want this quoted.
  440. configON.Add eventName, "@@" & code
  441. next
  442. ' set mergedConfig.Item("on") = configOn
  443. end if
  444. set configSettings = mergedConfig
  445. end function
  446. ''
  447. ' Returns a copy of a scripting.dictionary object
  448. '
  449. private function cloneDictionary( base )
  450. dim newOne, tmpKey
  451. Set newOne = CreateObject("Scripting.Dictionary")
  452. for each tmpKey in base
  453. newOne.Add tmpKey , base( tmpKey )
  454. next
  455. set cloneDictionary = newOne
  456. end function
  457. ''
  458. ' Combines two scripting.dictionary objects
  459. ' The base object isn't modified, and extra gets all the properties in base
  460. '
  461. private function mergeDictionary(base, extra)
  462. dim newOne, tmpKey
  463. for each tmpKey in base
  464. if not(extra.Exists( tmpKey )) then
  465. extra.Add tmpKey, base( tmpKey )
  466. end if
  467. next
  468. set mergeDictionary = extra
  469. end function
  470. ''
  471. ' Return global event handlers.
  472. '
  473. private function returnGlobalEvents()
  474. dim out, eventName, handlers
  475. dim handlersForEvent, handler, code, i
  476. out = ""
  477. if (isempty(CKEDITOR_returnedEvents)) then
  478. set CKEDITOR_returnedEvents = CreateObject("Scripting.Dictionary")
  479. end if
  480. for each eventName in oGlobalEvents
  481. handlers = oGlobalEvents( eventName )
  482. if not(CKEDITOR_returnedEvents.Exists(eventName)) then
  483. CKEDITOR_returnedEvents.Add eventName, CreateObject("Scripting.Dictionary")
  484. end if
  485. set handlersForEvent = CKEDITOR_returnedEvents.Item( eventName )
  486. ' handlersForEvent is another dictionary
  487. ' and handlers is an array
  488. for i = 0 to ubound(handlers)
  489. code = handlers( i )
  490. ' Return only new events
  491. if not(handlersForEvent.Exists( code )) then
  492. if (out <> "") then out = out & vbcrlf
  493. out = out & "CKEDITOR.on('" & eventName & "', " & code & ");"
  494. handlersForEvent.Add code, code
  495. end if
  496. next
  497. next
  498. returnGlobalEvents = out
  499. end function
  500. ''
  501. ' Initializes CKEditor (executed only once).
  502. '
  503. private function init()
  504. dim out, args, path, extraCode, file
  505. out = ""
  506. if (CKEDITOR_initComplete) then
  507. init = ""
  508. exit function
  509. end if
  510. if (initialized) then
  511. CKEDITOR_initComplete = true
  512. init = ""
  513. exit function
  514. end if
  515. args = ""
  516. path = ckeditorPath()
  517. if (timestamp <> "") and (timestamp <> "%" & "TIMESTAMP%") then
  518. args = "?t=" & timestamp
  519. end if
  520. ' Skip relative paths...
  521. if (instr(path, "..") <> 0) then
  522. out = out & script("window.CKEDITOR_BASEPATH='" & path & "';")
  523. end if
  524. out = out & "<scr" & "ipt type=""text/javascript"" src=""" & path & ckeditorFileName() & args & """></scr" & "ipt>" & vbcrlf
  525. extraCode = ""
  526. if (timestamp <> mTimeStamp) then
  527. extraCode = extraCode & "CKEDITOR.timestamp = '" & timestamp & "';"
  528. end if
  529. if (extraCode <> "") then
  530. out = out & script(extraCode)
  531. end if
  532. CKEDITOR_initComplete = true
  533. initialized = true
  534. init = out
  535. end function
  536. private function ckeditorFileName()
  537. ckeditorFileName = "ckeditor.js"
  538. end function
  539. ''
  540. ' Return path to ckeditor.js.
  541. '
  542. private function ckeditorPath()
  543. if (basePath <> "") then
  544. ckeditorPath = basePath
  545. else
  546. ' In classic ASP we can't get the location of this included script
  547. ckeditorPath = "/ckeditor/"
  548. end if
  549. ' Try to check if that folder contains the CKEditor files:
  550. ' If it's a full URL avoid checking it as it might point to an external server.
  551. if (instr(ckeditorPath, "://") <> 0) then exit function
  552. dim filename, oFSO, exists
  553. filename = server.mapPath(basePath & ckeditorFileName())
  554. set oFSO = Server.CreateObject("Scripting.FileSystemObject")
  555. exists = oFSO.FileExists(filename)
  556. set oFSO = nothing
  557. if not(exists) then
  558. response.clear
  559. response.write "<h1>CKEditor path validation failed</h1>"
  560. response.write "<p>The path &quot;" & ckeditorPath & "&quot; doesn't include the CKEditor main file (" & ckeditorFileName() & ")</p>"
  561. response.write "<p>Please, verify that you have set it correctly and/or adjust the 'basePath' property</p>"
  562. response.write "<p>Checked for physical file: &quot;" & filename & "&quot;</p>"
  563. response.end
  564. end if
  565. end function
  566. End Class
  567. ' URL: http://www.webdevbros.net/2007/04/26/generate-json-from-asp-datatypes/
  568. '**************************************************************************************************************
  569. '' @CLASSTITLE: JSON
  570. '' @CREATOR: Michal Gabrukiewicz (gabru at grafix.at), Michael Rebec
  571. '' @CONTRIBUTORS: - Cliff Pruitt (opensource at crayoncowboy.com)
  572. '' - Sylvain Lafontaine
  573. '' - Jef Housein
  574. '' - Jeremy Brown
  575. '' @CREATEDON: 2007-04-26 12:46
  576. '' @CDESCRIPTION: Comes up with functionality for JSON (http://json.org) to use within ASP.
  577. '' Correct escaping of characters, generating JSON Grammer out of ASP datatypes and structures
  578. '' Some examples (all use the <em>toJSON()</em> method but as it is the class' default method it can be left out):
  579. '' <code>
  580. '' <%
  581. '' 'simple number
  582. '' output = (new JSON)("myNum", 2, false)
  583. '' 'generates {"myNum": 2}
  584. ''
  585. '' 'array with different datatypes
  586. '' output = (new JSON)("anArray", array(2, "x", null), true)
  587. '' 'generates "anArray": [2, "x", null]
  588. '' '(note: the last parameter was true, thus no surrounding brackets in the result)
  589. '' % >
  590. '' </code>
  591. '' @REQUIRES: -
  592. '' @OPTIONEXPLICIT: yes
  593. '' @VERSION: 1.5.1
  594. '**************************************************************************************************************
  595. class JSON
  596. 'private members
  597. private output, innerCall
  598. '**********************************************************************************************************
  599. '* constructor
  600. '**********************************************************************************************************
  601. public sub class_initialize()
  602. newGeneration()
  603. end sub
  604. '******************************************************************************************
  605. '' @SDESCRIPTION: STATIC! takes a given string and makes it JSON valid
  606. '' @DESCRIPTION: all characters which needs to be escaped are beeing replaced by their
  607. '' unicode representation according to the
  608. '' RFC4627#2.5 - http://www.ietf.org/rfc/rfc4627.txt?number=4627
  609. '' @PARAM: val [string]: value which should be escaped
  610. '' @RETURN: [string] JSON valid string
  611. '******************************************************************************************
  612. public function escape(val)
  613. dim cDoubleQuote, cRevSolidus, cSolidus
  614. cDoubleQuote = &h22
  615. cRevSolidus = &h5C
  616. cSolidus = &h2F
  617. dim i, currentDigit
  618. for i = 1 to (len(val))
  619. currentDigit = mid(val, i, 1)
  620. if ascw(currentDigit) > &h00 and ascw(currentDigit) < &h1F then
  621. currentDigit = escapequence(currentDigit)
  622. elseif ascw(currentDigit) >= &hC280 and ascw(currentDigit) <= &hC2BF then
  623. currentDigit = "\u00" + right(padLeft(hex(ascw(currentDigit) - &hC200), 2, 0), 2)
  624. elseif ascw(currentDigit) >= &hC380 and ascw(currentDigit) <= &hC3BF then
  625. currentDigit = "\u00" + right(padLeft(hex(ascw(currentDigit) - &hC2C0), 2, 0), 2)
  626. else
  627. select case ascw(currentDigit)
  628. case cDoubleQuote: currentDigit = escapequence(currentDigit)
  629. case cRevSolidus: currentDigit = escapequence(currentDigit)
  630. case cSolidus: currentDigit = escapequence(currentDigit)
  631. end select
  632. end if
  633. escape = escape & currentDigit
  634. next
  635. end function
  636. '******************************************************************************************************************
  637. '' @SDESCRIPTION: generates a representation of a name value pair in JSON grammer
  638. '' @DESCRIPTION: It generates a name value pair which is represented as <em>{"name": value}</em> in JSON.
  639. '' the generation is fully recursive. Thus the value can also be a complex datatype (array in dictionary, etc.) e.g.
  640. '' <code>
  641. '' <%
  642. '' set j = new JSON
  643. '' j.toJSON "n", array(RS, dict, false), false
  644. '' j.toJSON "n", array(array(), 2, true), false
  645. '' % >
  646. '' </code>
  647. '' @PARAM: name [string]: name of the value (accessible with javascript afterwards). leave empty to get just the value
  648. '' @PARAM: val [variant], [int], [float], [array], [object], [dictionary]: value which needs
  649. '' to be generated. Conversation of the data types is as follows:<br>
  650. '' - <strong>ASP datatype -> JavaScript datatype</strong>
  651. '' - NOTHING, NULL -> null
  652. '' - INT, DOUBLE -> number
  653. '' - STRING -> string
  654. '' - BOOLEAN -> bool
  655. '' - ARRAY -> array
  656. '' - DICTIONARY -> Represents it as name value pairs. Each key is accessible as property afterwards. json will look like <code>"name": {"key1": "some value", "key2": "other value"}</code>
  657. '' - <em>multidimensional array</em> -> Generates a 1-dimensional array (flat) with all values of the multidimensional array
  658. '' - <em>request</em> object -> every property and collection (cookies, form, querystring, etc) of the asp request object is exposed as an item of a dictionary. Property names are <strong>lowercase</strong>. e.g. <em>servervariables</em>.
  659. '' - OBJECT -> name of the type (if unknown type) or all its properties (if class implements <em>reflect()</em> method)
  660. '' Implement a <strong>reflect()</strong> function if you want your custom classes to be recognized. The function must return
  661. '' a dictionary where the key holds the property name and the value its value. Example of a reflect function within a User class which has firstname and lastname properties
  662. '' <code>
  663. '' <%
  664. '' function reflect()
  665. '' . set reflect = server.createObject("scripting.dictionary")
  666. '' . reflect.add "firstname", firstname
  667. '' . reflect.add "lastname", lastname
  668. '' end function
  669. '' % >
  670. '' </code>
  671. '' Example of how to generate a JSON representation of the asp request object and access the <em>HTTP_HOST</em> server variable in JavaScript:
  672. '' <code>
  673. '' <script>alert(<%= (new JSON)(empty, request, false) % >.servervariables.HTTP_HOST);</script>
  674. '' </code>
  675. '' @PARAM: nested [bool]: indicates if the name value pair is already nested within another? if yes then the <em>{}</em> are left out.
  676. '' @RETURN: [string] returns a JSON representation of the given name value pair
  677. '******************************************************************************************************************
  678. public default function toJSON(name, val, nested)
  679. if not nested and not isEmpty(name) then write("{")
  680. if not isEmpty(name) then write("""" & escape(name) & """: ")
  681. generateValue(val)
  682. if not nested and not isEmpty(name) then write("}")
  683. toJSON = output
  684. if innerCall = 0 then newGeneration()
  685. end function
  686. '******************************************************************************************************************
  687. '* generate
  688. '******************************************************************************************************************
  689. private function generateValue(val)
  690. if isNull(val) then
  691. write("null")
  692. elseif isArray(val) then
  693. generateArray(val)
  694. elseif isObject(val) then
  695. dim tName : tName = typename(val)
  696. if val is nothing then
  697. write("null")
  698. elseif tName = "Dictionary" or tName = "IRequestDictionary" then
  699. generateDictionary(val)
  700. elseif tName = "IRequest" then
  701. set req = server.createObject("scripting.dictionary")
  702. req.add "clientcertificate", val.ClientCertificate
  703. req.add "cookies", val.cookies
  704. req.add "form", val.form
  705. req.add "querystring", val.queryString
  706. req.add "servervariables", val.serverVariables
  707. req.add "totalbytes", val.totalBytes
  708. generateDictionary(req)
  709. elseif tName = "IStringList" then
  710. if val.count = 1 then
  711. toJSON empty, val(1), true
  712. else
  713. generateArray(val)
  714. end if
  715. else
  716. generateObject(val)
  717. end if
  718. else
  719. 'bool
  720. dim varTyp
  721. varTyp = varType(val)
  722. if varTyp = 11 then
  723. if val then write("true") else write("false")
  724. 'int, long, byte
  725. elseif varTyp = 2 or varTyp = 3 or varTyp = 17 or varTyp = 19 then
  726. write(cLng(val))
  727. 'single, double, currency
  728. elseif varTyp = 4 or varTyp = 5 or varTyp = 6 or varTyp = 14 then
  729. write(replace(cDbl(val), ",", "."))
  730. else
  731. ' Using @@ at the beggining to signal JSON that we don't want this quoted.
  732. if left(val, 2) = "@@" then
  733. write( mid( val, 3 ) )
  734. else
  735. write("""" & escape(val & "") & """")
  736. end if
  737. end if
  738. end if
  739. generateValue = output
  740. end function
  741. '******************************************************************************************************************
  742. '* generateArray
  743. '******************************************************************************************************************
  744. private sub generateArray(val)
  745. dim item, i
  746. write("[")
  747. i = 0
  748. 'the for each allows us to support also multi dimensional arrays
  749. for each item in val
  750. if i > 0 then write(",")
  751. generateValue(item)
  752. i = i + 1
  753. next
  754. write("]")
  755. end sub
  756. '******************************************************************************************************************
  757. '* generateDictionary
  758. '******************************************************************************************************************
  759. private sub generateDictionary(val)
  760. innerCall = innerCall + 1
  761. if val.count = 0 then
  762. toJSON empty, null, true
  763. exit sub
  764. end if
  765. dim key, i
  766. write("{")
  767. i = 0
  768. for each key in val
  769. if i > 0 then write(",")
  770. toJSON key, val(key), true
  771. i = i + 1
  772. next
  773. write("}")
  774. innerCall = innerCall - 1
  775. end sub
  776. '******************************************************************************************************************
  777. '* generateObject
  778. '******************************************************************************************************************
  779. private sub generateObject(val)
  780. dim props
  781. on error resume next
  782. set props = val.reflect()
  783. if err = 0 then
  784. on error goto 0
  785. innerCall = innerCall + 1
  786. toJSON empty, props, true
  787. innerCall = innerCall - 1
  788. else
  789. on error goto 0
  790. write("""" & escape(typename(val)) & """")
  791. end if
  792. end sub
  793. '******************************************************************************************************************
  794. '* newGeneration
  795. '******************************************************************************************************************
  796. private sub newGeneration()
  797. output = empty
  798. innerCall = 0
  799. end sub
  800. '******************************************************************************************
  801. '* JsonEscapeSquence
  802. '******************************************************************************************
  803. private function escapequence(digit)
  804. escapequence = "\u00" + right(padLeft(hex(ascw(digit)), 2, 0), 2)
  805. end function
  806. '******************************************************************************************
  807. '* padLeft
  808. '******************************************************************************************
  809. private function padLeft(value, totalLength, paddingChar)
  810. padLeft = right(clone(paddingChar, totalLength) & value, totalLength)
  811. end function
  812. '******************************************************************************************
  813. '* clone
  814. '******************************************************************************************
  815. private function clone(byVal str, n)
  816. dim i
  817. for i = 1 to n : clone = clone & str : next
  818. end function
  819. '******************************************************************************************
  820. '* write
  821. '******************************************************************************************
  822. private sub write(val)
  823. output = output & val
  824. end sub
  825. end class
  826. %>