minor bug fix
[openemr.git] / library / custom_template / ckeditor / ckeditor.asp
blob97bb808c96658b8d181206b23bc6c5d910766fce
1 <%
3 ' Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
4 ' For licensing, see LICENSE.html or http://ckeditor.com/license
6 ' Shared variable for all instances ("static")
7 dim CKEDITOR_initComplete
8 dim CKEDITOR_returnedEvents
11 ' \brief CKEditor class that can be used to create editor
12 ' instances in ASP pages on server side.
13 ' @see http://ckeditor.com
15 ' Sample usage:
16 ' @code
17 ' editor = new CKEditor
18 ' editor.editor "editor1", "<p>Initial value.</p>", empty, empty
19 ' @endcode
21 Class CKEditor
24 ' The version of %CKEditor.
25 private version
28 ' A constant string unique for each release of %CKEditor.
29 private mTimeStamp
32 ' URL to the %CKEditor installation directory (absolute or relative to document root).
33 ' If not set, CKEditor will try to guess it's path.
35 ' Example usage:
36 ' @code
37 ' editor.basePath = "/ckeditor/"
38 ' @endcode
39 Public basePath
42 ' A boolean variable indicating whether CKEditor has been initialized.
43 ' Set it to true only if you have already included
44 ' &lt;script&gt; tag loading ckeditor.js in your website.
45 Public initialized
48 ' Boolean variable indicating whether created code should be printed out or returned by a function.
50 ' Example 1: get the code creating %CKEditor instance and print it on a page with the "echo" function.
51 ' @code
52 ' editor = new CKEditor
53 ' editor.returnOutput = true
54 ' code = editor.editor("editor1", "<p>Initial value.</p>", empty, empty)
55 ' response.write "<p>Editor 1:</p>"
56 ' response.write code
57 ' @endcode
58 Public returnOutput
61 ' A Dictionary with textarea attributes.
63 ' When %CKEditor is created with the editor() method, a HTML &lt;textarea&gt; element is created,
64 ' it will be displayed to anyone with JavaScript disabled or with incompatible browser.
65 public textareaAttributes
68 ' A string indicating the creation date of %CKEditor.
69 ' Do not change it unless you want to force browsers to not use previously cached version of %CKEditor.
70 public timestamp
73 ' A dictionary that holds the instance configuration.
74 private oInstanceConfig
77 ' A dictionary that holds the configuration for all the instances.
78 private oAllInstancesConfig
81 ' A dictionary that holds event listeners for the instance.
82 private oInstanceEvents
85 ' A dictionary that holds event listeners for all the instances.
86 private oAllInstancesEvents
89 ' A Dictionary that holds global event listeners (CKEDITOR object)
90 private oGlobalEvents
93 Private Sub Class_Initialize()
94 version = "3.5.2"
95 timeStamp = "B1GG4Z6"
96 mTimeStamp = "B1GG4Z6"
98 Set oInstanceConfig = CreateObject("Scripting.Dictionary")
99 Set oAllInstancesConfig = CreateObject("Scripting.Dictionary")
101 Set oInstanceEvents = CreateObject("Scripting.Dictionary")
102 Set oAllInstancesEvents = CreateObject("Scripting.Dictionary")
103 Set oGlobalEvents = CreateObject("Scripting.Dictionary")
105 Set textareaAttributes = CreateObject("Scripting.Dictionary")
106 textareaAttributes.Add "rows", 8
107 textareaAttributes.Add "cols", 60
108 End Sub
111 ' Creates a %CKEditor instance.
112 ' In incompatible browsers %CKEditor will downgrade to plain HTML &lt;textarea&gt; element.
114 ' @param name (string) Name of the %CKEditor instance (this will be also the "name" attribute of textarea element).
115 ' @param value (string) Initial value.
117 ' Example usage:
118 ' @code
119 ' set editor = New CKEditor
120 ' editor.editor "field1", "<p>Initial value.</p>"
121 ' @endcode
123 ' Advanced example:
124 ' @code
125 ' set editor = new CKEditor
126 ' set config = CreateObject("Scripting.Dictionary")
127 ' config.Add "toolbar", Array( _
128 ' Array( "Source", "-", "Bold", "Italic", "Underline", "Strike" ), _
129 ' Array( "Image", "Link", "Unlink", "Anchor" ) _
131 ' set events = CreateObject("Scripting.Dictionary")
132 ' events.Add "instanceReady", "function (evt) { alert('Loaded second editor: ' + evt.editor.name );}"
134 ' editor.editor "field1", "<p>Initial value.</p>", config, events
135 ' @endcode
137 public function editor(name, value)
138 dim attr, out, js, customConfig, extraConfig
139 dim attribute
141 attr = ""
143 for each attribute in textareaAttributes
144 attr = attr & " " & attribute & "=""" & replace( textareaAttributes( attribute ), """", "&quot" ) & """"
145 next
147 out = "<textarea name=""" & name & """" & attr & ">" & Server.HtmlEncode(value) & "</textarea>" & vbcrlf
149 if not(initialized) then
150 out = out & init()
151 end if
153 set customConfig = configSettings()
154 js = returnGlobalEvents()
156 extraConfig = (new JSON)( empty, customConfig, false )
157 if extraConfig<>"" then extraConfig = ", " & extraConfig
158 js = js & "CKEDITOR.replace('" & name & "'" & extraConfig & ");"
160 out = out & script(js)
162 if not(returnOutput) then
163 response.write out
164 out = ""
165 end if
167 editor = out
169 oInstanceConfig.RemoveAll
170 oInstanceEvents.RemoveAll
171 end function
174 ' Replaces a &lt;textarea&gt; with a %CKEditor instance.
176 ' @param id (string) The id or name of textarea element.
178 ' Example 1: adding %CKEditor to &lt;textarea name="article"&gt;&lt;/textarea&gt; element:
179 ' @code
180 ' set editor = New CKEditor
181 ' editor.replace "article"
182 ' @endcode
184 public function replaceInstance(id)
185 dim out, js, customConfig, extraConfig
187 out = ""
188 if not(initialized) then
189 out = out & init()
190 end if
192 set customConfig = configSettings()
193 js = returnGlobalEvents()
195 extraConfig = (new JSON)( empty, customConfig, false )
196 if extraConfig<>"" then extraConfig = ", " & extraConfig
197 js = js & "CKEDITOR.replace('" & id & "'" & extraConfig & ");"
199 out = out & script(js)
201 if not(returnOutput) then
202 response.write out
203 out = ""
204 end if
206 replaceInstance = out
208 oInstanceConfig.RemoveAll
209 oInstanceEvents.RemoveAll
210 end function
213 ' Replace all &lt;textarea&gt; elements available in the document with editor instances.
215 ' @param className (string) If set, replace all textareas with class className in the page.
217 ' Example 1: replace all &lt;textarea&gt; elements in the page.
218 ' @code
219 ' editor = new CKEditor
220 ' editor.replaceAll empty
221 ' @endcode
223 ' Example 2: replace all &lt;textarea class="myClassName"&gt; elements in the page.
224 ' @code
225 ' editor = new CKEditor
226 ' editor.replaceAll 'myClassName'
227 ' @endcode
229 function replaceAll(className)
230 dim out, js, customConfig
232 out = ""
233 if not(initialized) then
234 out = out & init()
235 end if
237 set customConfig = configSettings()
238 js = returnGlobalEvents()
240 if (customConfig.Count=0) then
241 if (isEmpty(className)) then
242 js = js & "CKEDITOR.replaceAll();"
243 else
244 js = js & "CKEDITOR.replaceAll('" & className & "');"
245 end if
246 else
247 js = js & "CKEDITOR.replaceAll( function(textarea, config) {\n"
248 if not(isEmpty(className)) then
249 js = js & " var classRegex = new RegExp('(?:^| )' + '" & className & "' + '(?:$| )');\n"
250 js = js & " if (!classRegex.test(textarea.className))\n"
251 js = js & " return false;\n"
252 end if
253 js = js & " CKEDITOR.tools.extend(config, " & (new JSON)( empty, customConfig, false ) & ", true);"
254 js = js & "} );"
255 end if
257 out = out & script(js)
259 if not(returnOutput) then
260 response.write out
261 out = ""
262 end if
264 replaceAll = out
266 oInstanceConfig.RemoveAll
267 oInstanceEvents.RemoveAll
268 end function
272 ' A Dictionary that holds the %CKEditor configuration for all instances
273 ' For the list of available options, see http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.config.html
275 ' Example usage:
276 ' @code
277 ' editor.config("height") = 400
278 ' // Use @@ at the beggining of a string to ouput it without surrounding quotes.
279 ' editor.config("width") = "@@screen.width * 0.8"
280 ' @endcode
281 Public Property Let Config( configKey, configValue )
282 oAllInstancesConfig.Add configKey, configValue
283 End Property
286 ' Configuration options for the next instance
288 Public Property Let instanceConfig( configKey, configValue )
289 oInstanceConfig.Add configKey, configValue
290 End Property
293 ' Adds event listener.
294 ' Events are fired by %CKEditor in various situations.
296 ' @param eventName (string) Event name.
297 ' @param javascriptCode (string) Javascript anonymous function or function name.
299 ' Example usage:
300 ' @code
301 ' editor.addEventHandler "instanceReady", "function (ev) { " & _
302 ' " alert('Loaded: ' + ev.editor.name); " & _
303 ' "}"
304 ' @endcode
306 public sub addEventHandler(eventName, javascriptCode)
307 if not(oAllInstancesEvents.Exists( eventName ) ) then
308 oAllInstancesEvents.Add eventName, Array()
309 end if
311 dim listeners, size
312 listeners = oAllInstancesEvents( eventName )
313 size = ubound(listeners) + 1
314 redim preserve listeners(size)
315 listeners(size) = javascriptCode
317 oAllInstancesEvents( eventName ) = listeners
318 ' '' Avoid duplicates. fixme...
319 ' if (!in_array($javascriptCode, $this->_events[$event])) {
320 ' $this->_events[$event][] = $javascriptCode;
322 end sub
325 ' Clear registered event handlers.
326 ' Note: this function will have no effect on already created editor instances.
328 ' @param eventName (string) Event name, if set to 'empty' all event handlers will be removed.
330 public sub clearEventHandlers( eventName )
331 if not(isEmpty( eventName )) then
332 oAllInstancesEvents.Remove eventName
333 else
334 oAllInstancesEvents.RemoveAll
335 end if
336 end sub
340 ' Adds event listener only for the next instance.
341 ' Events are fired by %CKEditor in various situations.
343 ' @param eventName (string) Event name.
344 ' @param javascriptCode (string) Javascript anonymous function or function name.
346 ' Example usage:
347 ' @code
348 ' editor.addInstanceEventHandler "instanceReady", "function (ev) { " & _
349 ' " alert('Loaded: ' + ev.editor.name); " & _
350 ' "}"
351 ' @endcode
353 public sub addInstanceEventHandler(eventName, javascriptCode)
354 if not(oInstanceEvents.Exists( eventName ) ) then
355 oInstanceEvents.Add eventName, Array()
356 end if
358 dim listeners, size
359 listeners = oInstanceEvents( eventName )
360 size = ubound(listeners) + 1
361 redim preserve listeners(size)
362 listeners(size) = javascriptCode
364 oInstanceEvents( eventName ) = listeners
365 ' '' Avoid duplicates. fixme...
366 ' if (!in_array($javascriptCode, $this->_events[$event])) {
367 ' $this->_events[$event][] = $javascriptCode;
369 end sub
372 ' Clear registered event handlers.
373 ' Note: this function will have no effect on already created editor instances.
375 ' @param eventName (string) Event name, if set to 'empty' all event handlers will be removed.
377 public sub clearInstanceEventHandlers( eventName )
378 if not(isEmpty( eventName )) then
379 oInstanceEvents.Remove eventName
380 else
381 oInstanceEvents.RemoveAll
382 end if
383 end sub
386 ' Adds global event listener.
388 ' @param event (string) Event name.
389 ' @param javascriptCode (string) Javascript anonymous function or function name.
391 ' Example usage:
392 ' @code
393 ' editor.addGlobalEventHandler "dialogDefinition", "function (ev) { " & _
394 ' " alert('Loading dialog: ' + ev.data.name); " & _
395 ' "}"
396 ' @endcode
398 public sub addGlobalEventHandler( eventName, javascriptCode)
399 if not(oGlobalEvents.Exists( eventName ) ) then
400 oGlobalEvents.Add eventName, Array()
401 end if
403 dim listeners, size
404 listeners = oGlobalEvents( eventName )
405 size = ubound(listeners) + 1
406 redim preserve listeners(size)
407 listeners(size) = javascriptCode
409 oGlobalEvents( eventName ) = listeners
411 ' // Avoid duplicates.
412 ' if (!in_array($javascriptCode, $this->_globalEvents[$event])) {
413 ' $this->_globalEvents[$event][] = $javascriptCode;
415 end sub
418 ' Clear registered global event handlers.
419 ' Note: this function will have no effect if the event handler has been already printed/returned.
421 ' @param eventName (string) Event name, if set to 'empty' all event handlers will be removed .
423 public sub clearGlobalEventHandlers( eventName )
424 if not(isEmpty( eventName )) then
425 oGlobalEvents.Remove eventName
426 else
427 oGlobalEvents.RemoveAll
428 end if
429 end sub
432 ' Prints javascript code.
434 ' @param string js
436 private function script(js)
437 script = "<script type=""text/javascript"">" & _
438 "//<![CDATA[" & vbcrlf & _
439 js & vbcrlf & _
440 "//]]>" & _
441 "</script>" & vbcrlf
442 end function
445 ' Returns the configuration array (global and instance specific settings are merged into one array).
447 ' @param instanceConfig (Dictionary) The specific configurations to apply to editor instance.
448 ' @param instanceEvents (Dictionary) Event listeners for editor instance.
450 private function configSettings()
451 dim mergedConfig, mergedEvents
452 set mergedConfig = cloneDictionary(oAllInstancesConfig)
453 set mergedEvents = cloneDictionary(oAllInstancesEvents)
455 if not(isEmpty(oInstanceConfig)) then
456 set mergedConfig = mergeDictionary(mergedConfig, oInstanceConfig)
457 end if
459 if not(isEmpty(oInstanceEvents)) then
460 for each eventName in oInstanceEvents
461 code = oInstanceEvents( eventName )
463 if not(mergedEvents.Exists( eventName)) then
464 mergedEvents.Add eventName, code
465 else
467 dim listeners, size
468 listeners = mergedEvents( eventName )
469 size = ubound(listeners)
470 if isArray( code ) then
471 addedCount = ubound(code)
472 redim preserve listeners( size + addedCount + 1 )
473 for i = 0 to addedCount
474 listeners(size + i + 1) = code (i)
475 next
476 else
477 size = size + 1
478 redim preserve listeners(size)
479 listeners(size) = code
480 end if
482 mergedEvents( eventName ) = listeners
483 end if
484 next
486 end if
488 dim i, eventName, handlers, configON, ub, code
490 if mergedEvents.Count>0 then
491 if mergedConfig.Exists( "on" ) then
492 set configON = mergedConfig.items( "on" )
493 else
494 set configON = CreateObject("Scripting.Dictionary")
495 mergedConfig.Add "on", configOn
496 end if
498 for each eventName in mergedEvents
499 handlers = mergedEvents( eventName )
500 code = ""
502 if isArray(handlers) then
503 uB = ubound(handlers)
504 if (uB = 0) then
505 code = handlers(0)
506 else
507 code = "function (ev) {"
508 for i=0 to uB
509 code = code & "(" & handlers(i) & ")(ev);"
510 next
511 code = code & "}"
512 end if
513 else
514 code = handlers
515 end if
516 ' Using @@ at the beggining to signal JSON that we don't want this quoted.
517 configON.Add eventName, "@@" & code
518 next
520 ' set mergedConfig.Item("on") = configOn
521 end if
523 set configSettings = mergedConfig
524 end function
527 ' Returns a copy of a scripting.dictionary object
529 private function cloneDictionary( base )
530 dim newOne, tmpKey
532 Set newOne = CreateObject("Scripting.Dictionary")
533 for each tmpKey in base
534 newOne.Add tmpKey , base( tmpKey )
535 next
537 set cloneDictionary = newOne
538 end function
541 ' Combines two scripting.dictionary objects
542 ' The base object isn't modified, and extra gets all the properties in base
544 private function mergeDictionary(base, extra)
545 dim newOne, tmpKey
547 for each tmpKey in base
548 if not(extra.Exists( tmpKey )) then
549 extra.Add tmpKey, base( tmpKey )
550 end if
551 next
553 set mergeDictionary = extra
554 end function
557 ' Return global event handlers.
559 private function returnGlobalEvents()
560 dim out, eventName, handlers
561 dim handlersForEvent, handler, code, i
562 out = ""
564 if (isempty(CKEDITOR_returnedEvents)) then
565 set CKEDITOR_returnedEvents = CreateObject("Scripting.Dictionary")
566 end if
568 for each eventName in oGlobalEvents
569 handlers = oGlobalEvents( eventName )
571 if not(CKEDITOR_returnedEvents.Exists(eventName)) then
572 CKEDITOR_returnedEvents.Add eventName, CreateObject("Scripting.Dictionary")
573 end if
575 set handlersForEvent = CKEDITOR_returnedEvents.Item( eventName )
577 ' handlersForEvent is another dictionary
578 ' and handlers is an array
580 for i = 0 to ubound(handlers)
581 code = handlers( i )
583 ' Return only new events
584 if not(handlersForEvent.Exists( code )) then
585 if (out <> "") then out = out & vbcrlf
586 out = out & "CKEDITOR.on('" & eventName & "', " & code & ");"
587 handlersForEvent.Add code, code
588 end if
589 next
590 next
592 returnGlobalEvents = out
593 end function
596 ' Initializes CKEditor (executed only once).
598 private function init()
599 dim out, args, path, extraCode, file
600 out = ""
602 if (CKEDITOR_initComplete) then
603 init = ""
604 exit function
605 end if
607 if (initialized) then
608 CKEDITOR_initComplete = true
609 init = ""
610 exit function
611 end if
613 args = ""
614 path = ckeditorPath()
616 if (timestamp <> "") and (timestamp <> "%" & "TIMESTAMP%") then
617 args = "?t=" & timestamp
618 end if
620 ' Skip relative paths...
621 if (instr(path, "..") <> 0) then
622 out = out & script("window.CKEDITOR_BASEPATH='" & path & "';")
623 end if
625 out = out & "<scr" & "ipt type=""text/javascript"" src=""" & path & ckeditorFileName() & args & """></scr" & "ipt>" & vbcrlf
627 extraCode = ""
628 if (timestamp <> mTimeStamp) then
629 extraCode = extraCode & "CKEDITOR.timestamp = '" & timestamp & "';"
630 end if
631 if (extraCode <> "") then
632 out = out & script(extraCode)
633 end if
635 CKEDITOR_initComplete = true
636 initialized = true
638 init = out
639 end function
641 private function ckeditorFileName()
642 ckeditorFileName = "ckeditor.js"
643 end function
646 ' Return path to ckeditor.js.
648 private function ckeditorPath()
649 if (basePath <> "") then
650 ckeditorPath = basePath
651 else
652 ' In classic ASP we can't get the location of this included script
653 ckeditorPath = "/ckeditor/"
654 end if
656 ' Try to check if that folder contains the CKEditor files:
657 ' If it's a full URL avoid checking it as it might point to an external server.
658 if (instr(ckeditorPath, "://") <> 0) then exit function
660 dim filename, oFSO, exists
661 filename = server.mapPath(basePath & ckeditorFileName())
662 set oFSO = Server.CreateObject("Scripting.FileSystemObject")
663 exists = oFSO.FileExists(filename)
664 set oFSO = nothing
666 if not(exists) then
667 response.clear
668 response.write "<h1>CKEditor path validation failed</h1>"
669 response.write "<p>The path &quot;" & ckeditorPath & "&quot; doesn't include the CKEditor main file (" & ckeditorFileName() & ")</p>"
670 response.write "<p>Please, verify that you have set it correctly and/or adjust the 'basePath' property</p>"
671 response.write "<p>Checked for physical file: &quot;" & filename & "&quot;</p>"
672 response.end
673 end if
674 end function
676 End Class
680 ' URL: http://www.webdevbros.net/2007/04/26/generate-json-from-asp-datatypes/
681 '**************************************************************************************************************
682 '' @CLASSTITLE: JSON
683 '' @CREATOR: Michal Gabrukiewicz (gabru at grafix.at), Michael Rebec
684 '' @CONTRIBUTORS: - Cliff Pruitt (opensource at crayoncowboy.com)
685 '' - Sylvain Lafontaine
686 '' - Jef Housein
687 '' - Jeremy Brown
688 '' @CREATEDON: 2007-04-26 12:46
689 '' @CDESCRIPTION: Comes up with functionality for JSON (http://json.org) to use within ASP.
690 '' Correct escaping of characters, generating JSON Grammer out of ASP datatypes and structures
691 '' Some examples (all use the <em>toJSON()</em> method but as it is the class' default method it can be left out):
692 '' <code>
693 '' <%
694 '' 'simple number
695 '' output = (new JSON)("myNum", 2, false)
696 '' 'generates {"myNum": 2}
698 '' 'array with different datatypes
699 '' output = (new JSON)("anArray", array(2, "x", null), true)
700 '' 'generates "anArray": [2, "x", null]
701 '' '(note: the last parameter was true, thus no surrounding brackets in the result)
702 '' % >
703 '' </code>
704 '' @REQUIRES: -
705 '' @OPTIONEXPLICIT: yes
706 '' @VERSION: 1.5.1
708 '**************************************************************************************************************
709 class JSON
711 'private members
712 private output, innerCall
714 '**********************************************************************************************************
715 '* constructor
716 '**********************************************************************************************************
717 public sub class_initialize()
718 newGeneration()
719 end sub
721 '******************************************************************************************
722 '' @SDESCRIPTION: STATIC! takes a given string and makes it JSON valid
723 '' @DESCRIPTION: all characters which needs to be escaped are beeing replaced by their
724 '' unicode representation according to the
725 '' RFC4627#2.5 - http://www.ietf.org/rfc/rfc4627.txt?number=4627
726 '' @PARAM: val [string]: value which should be escaped
727 '' @RETURN: [string] JSON valid string
728 '******************************************************************************************
729 public function escape(val)
730 dim cDoubleQuote, cRevSolidus, cSolidus
731 cDoubleQuote = &h22
732 cRevSolidus = &h5C
733 cSolidus = &h2F
734 dim i, currentDigit
735 for i = 1 to (len(val))
736 currentDigit = mid(val, i, 1)
737 if ascw(currentDigit) > &h00 and ascw(currentDigit) < &h1F then
738 currentDigit = escapequence(currentDigit)
739 elseif ascw(currentDigit) >= &hC280 and ascw(currentDigit) <= &hC2BF then
740 currentDigit = "\u00" + right(padLeft(hex(ascw(currentDigit) - &hC200), 2, 0), 2)
741 elseif ascw(currentDigit) >= &hC380 and ascw(currentDigit) <= &hC3BF then
742 currentDigit = "\u00" + right(padLeft(hex(ascw(currentDigit) - &hC2C0), 2, 0), 2)
743 else
744 select case ascw(currentDigit)
745 case cDoubleQuote: currentDigit = escapequence(currentDigit)
746 case cRevSolidus: currentDigit = escapequence(currentDigit)
747 case cSolidus: currentDigit = escapequence(currentDigit)
748 end select
749 end if
750 escape = escape & currentDigit
751 next
752 end function
754 '******************************************************************************************************************
755 '' @SDESCRIPTION: generates a representation of a name value pair in JSON grammer
756 '' @DESCRIPTION: It generates a name value pair which is represented as <em>{"name": value}</em> in JSON.
757 '' the generation is fully recursive. Thus the value can also be a complex datatype (array in dictionary, etc.) e.g.
758 '' <code>
759 '' <%
760 '' set j = new JSON
761 '' j.toJSON "n", array(RS, dict, false), false
762 '' j.toJSON "n", array(array(), 2, true), false
763 '' % >
764 '' </code>
765 '' @PARAM: name [string]: name of the value (accessible with javascript afterwards). leave empty to get just the value
766 '' @PARAM: val [variant], [int], [float], [array], [object], [dictionary]: value which needs
767 '' to be generated. Conversation of the data types is as follows:<br>
768 '' - <strong>ASP datatype -> JavaScript datatype</strong>
769 '' - NOTHING, NULL -> null
770 '' - INT, DOUBLE -> number
771 '' - STRING -> string
772 '' - BOOLEAN -> bool
773 '' - ARRAY -> array
774 '' - 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>
775 '' - <em>multidimensional array</em> -> Generates a 1-dimensional array (flat) with all values of the multidimensional array
776 '' - <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>.
777 '' - OBJECT -> name of the type (if unknown type) or all its properties (if class implements <em>reflect()</em> method)
778 '' Implement a <strong>reflect()</strong> function if you want your custom classes to be recognized. The function must return
779 '' 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
780 '' <code>
781 '' <%
782 '' function reflect()
783 '' . set reflect = server.createObject("scripting.dictionary")
784 '' . reflect.add "firstname", firstname
785 '' . reflect.add "lastname", lastname
786 '' end function
787 '' % >
788 '' </code>
789 '' Example of how to generate a JSON representation of the asp request object and access the <em>HTTP_HOST</em> server variable in JavaScript:
790 '' <code>
791 '' <script>alert(<%= (new JSON)(empty, request, false) % >.servervariables.HTTP_HOST);</script>
792 '' </code>
793 '' @PARAM: nested [bool]: indicates if the name value pair is already nested within another? if yes then the <em>{}</em> are left out.
794 '' @RETURN: [string] returns a JSON representation of the given name value pair
795 '******************************************************************************************************************
796 public default function toJSON(name, val, nested)
797 if not nested and not isEmpty(name) then write("{")
798 if not isEmpty(name) then write("""" & escape(name) & """: ")
799 generateValue(val)
800 if not nested and not isEmpty(name) then write("}")
801 toJSON = output
803 if innerCall = 0 then newGeneration()
804 end function
806 '******************************************************************************************************************
807 '* generate
808 '******************************************************************************************************************
809 private function generateValue(val)
810 if isNull(val) then
811 write("null")
812 elseif isArray(val) then
813 generateArray(val)
814 elseif isObject(val) then
815 dim tName : tName = typename(val)
816 if val is nothing then
817 write("null")
818 elseif tName = "Dictionary" or tName = "IRequestDictionary" then
819 generateDictionary(val)
820 elseif tName = "IRequest" then
821 set req = server.createObject("scripting.dictionary")
822 req.add "clientcertificate", val.ClientCertificate
823 req.add "cookies", val.cookies
824 req.add "form", val.form
825 req.add "querystring", val.queryString
826 req.add "servervariables", val.serverVariables
827 req.add "totalbytes", val.totalBytes
828 generateDictionary(req)
829 elseif tName = "IStringList" then
830 if val.count = 1 then
831 toJSON empty, val(1), true
832 else
833 generateArray(val)
834 end if
835 else
836 generateObject(val)
837 end if
838 else
839 'bool
840 dim varTyp
841 varTyp = varType(val)
842 if varTyp = 11 then
843 if val then write("true") else write("false")
844 'int, long, byte
845 elseif varTyp = 2 or varTyp = 3 or varTyp = 17 or varTyp = 19 then
846 write(cLng(val))
847 'single, double, currency
848 elseif varTyp = 4 or varTyp = 5 or varTyp = 6 or varTyp = 14 then
849 write(replace(cDbl(val), ",", "."))
850 else
851 ' Using @@ at the beggining to signal JSON that we don't want this quoted.
852 if left(val, 2) = "@@" then
853 write( mid( val, 3 ) )
854 else
855 write("""" & escape(val & "") & """")
856 end if
857 end if
858 end if
859 generateValue = output
860 end function
862 '******************************************************************************************************************
863 '* generateArray
864 '******************************************************************************************************************
865 private sub generateArray(val)
866 dim item, i
867 write("[")
868 i = 0
869 'the for each allows us to support also multi dimensional arrays
870 for each item in val
871 if i > 0 then write(",")
872 generateValue(item)
873 i = i + 1
874 next
875 write("]")
876 end sub
878 '******************************************************************************************************************
879 '* generateDictionary
880 '******************************************************************************************************************
881 private sub generateDictionary(val)
882 innerCall = innerCall + 1
883 if val.count = 0 then
884 toJSON empty, null, true
885 exit sub
886 end if
887 dim key, i
888 write("{")
889 i = 0
890 for each key in val
891 if i > 0 then write(",")
892 toJSON key, val(key), true
893 i = i + 1
894 next
895 write("}")
896 innerCall = innerCall - 1
897 end sub
899 '******************************************************************************************************************
900 '* generateObject
901 '******************************************************************************************************************
902 private sub generateObject(val)
903 dim props
904 on error resume next
905 set props = val.reflect()
906 if err = 0 then
907 on error goto 0
908 innerCall = innerCall + 1
909 toJSON empty, props, true
910 innerCall = innerCall - 1
911 else
912 on error goto 0
913 write("""" & escape(typename(val)) & """")
914 end if
915 end sub
917 '******************************************************************************************************************
918 '* newGeneration
919 '******************************************************************************************************************
920 private sub newGeneration()
921 output = empty
922 innerCall = 0
923 end sub
925 '******************************************************************************************
926 '* JsonEscapeSquence
927 '******************************************************************************************
928 private function escapequence(digit)
929 escapequence = "\u00" + right(padLeft(hex(ascw(digit)), 2, 0), 2)
930 end function
932 '******************************************************************************************
933 '* padLeft
934 '******************************************************************************************
935 private function padLeft(value, totalLength, paddingChar)
936 padLeft = right(clone(paddingChar, totalLength) & value, totalLength)
937 end function
939 '******************************************************************************************
940 '* clone
941 '******************************************************************************************
942 private function clone(byVal str, n)
943 dim i
944 for i = 1 to n : clone = clone & str : next
945 end function
947 '******************************************************************************************
948 '* write
949 '******************************************************************************************
950 private sub write(val)
951 output = output & val
952 end sub
954 end class