Mimimum syndical pour en faire un produit zope / cmf.
[ckeditor.git] / ckeditor.asp
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
6 ' Shared variable for all instances ("static")
7 dim CKEDITOR_initComplete
8 dim CKEDITOR_returnedEvents
9
10 ''
11 ' \brief CKEditor class that can be used to create editor
12 ' instances in ASP pages on server side.
13 ' @see http://ckeditor.com
14 '
15 ' Sample usage:
16 ' @code
17 ' editor = new CKEditor
18 ' editor.editor "editor1", "<p>Initial value.</p>", empty, empty
19 ' @endcode
20
21 Class CKEditor
22
23 ''
24 ' The version of %CKEditor.
25 private version
26
27 ''
28 ' A constant string unique for each release of %CKEditor.
29 private mTimeStamp
30
31 ''
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.
34 '
35 ' Example usage:
36 ' @code
37 ' editor.basePath = "/ckeditor/"
38 ' @endcode
39 Public basePath
40
41 ''
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
46
47 ''
48 ' Boolean variable indicating whether created code should be printed out or returned by a function.
49 '
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
59
60 ''
61 ' A Dictionary with textarea attributes.
62 '
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
66
67 ''
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
71
72 ''
73 ' A dictionary that holds the instance configuration.
74 private oInstanceConfig
75
76 ''
77 ' A dictionary that holds the configuration for all the instances.
78 private oAllInstancesConfig
79
80 ''
81 ' A dictionary that holds event listeners for the instance.
82 private oInstanceEvents
83
84 ''
85 ' A dictionary that holds event listeners for all the instances.
86 private oAllInstancesEvents
87
88 ''
89 ' A Dictionary that holds global event listeners (CKEDITOR object)
90 private oGlobalEvents
91
92
93 Private Sub Class_Initialize()
94 version = "3.6.1"
95 timeStamp = "B5GJ5GG"
96 mTimeStamp = "B5GJ5GG"
97
98 Set oInstanceConfig = CreateObject("Scripting.Dictionary")
99 Set oAllInstancesConfig = CreateObject("Scripting.Dictionary")
100
101 Set oInstanceEvents = CreateObject("Scripting.Dictionary")
102 Set oAllInstancesEvents = CreateObject("Scripting.Dictionary")
103 Set oGlobalEvents = CreateObject("Scripting.Dictionary")
104
105 Set textareaAttributes = CreateObject("Scripting.Dictionary")
106 textareaAttributes.Add "rows", 8
107 textareaAttributes.Add "cols", 60
108 End Sub
109
110 ''
111 ' Creates a %CKEditor instance.
112 ' In incompatible browsers %CKEditor will downgrade to plain HTML &lt;textarea&gt; element.
113 '
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.
116 '
117 ' Example usage:
118 ' @code
119 ' set editor = New CKEditor
120 ' editor.editor "field1", "<p>Initial value.</p>"
121 ' @endcode
122 '
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" ) _
130 ' )
131 ' set events = CreateObject("Scripting.Dictionary")
132 ' events.Add "instanceReady", "function (evt) { alert('Loaded second editor: ' + evt.editor.name );}"
133
134 ' editor.editor "field1", "<p>Initial value.</p>", config, events
135 ' @endcode
136 '
137 public function editor(name, value)
138 dim attr, out, js, customConfig, extraConfig
139 dim attribute
140
141 attr = ""
142
143 for each attribute in textareaAttributes
144 attr = attr & " " & attribute & "=""" & replace( textareaAttributes( attribute ), """", "&quot" ) & """"
145 next
146
147 out = "<textarea name=""" & name & """" & attr & ">" & Server.HtmlEncode(value) & "</textarea>" & vbcrlf
148
149 if not(initialized) then
150 out = out & init()
151 end if
152
153 set customConfig = configSettings()
154 js = returnGlobalEvents()
155
156 extraConfig = (new JSON)( empty, customConfig, false )
157 if extraConfig<>"" then extraConfig = ", " & extraConfig
158 js = js & "CKEDITOR.replace('" & name & "'" & extraConfig & ");"
159
160 out = out & script(js)
161
162 if not(returnOutput) then
163 response.write out
164 out = ""
165 end if
166
167 editor = out
168
169 oInstanceConfig.RemoveAll
170 oInstanceEvents.RemoveAll
171 end function
172
173 ''
174 ' Replaces a &lt;textarea&gt; with a %CKEditor instance.
175 '
176 ' @param id (string) The id or name of textarea element.
177 '
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
183 '
184 public function replaceInstance(id)
185 dim out, js, customConfig, extraConfig
186
187 out = ""
188 if not(initialized) then
189 out = out & init()
190 end if
191
192 set customConfig = configSettings()
193 js = returnGlobalEvents()
194
195 extraConfig = (new JSON)( empty, customConfig, false )
196 if extraConfig<>"" then extraConfig = ", " & extraConfig
197 js = js & "CKEDITOR.replace('" & id & "'" & extraConfig & ");"
198
199 out = out & script(js)
200
201 if not(returnOutput) then
202 response.write out
203 out = ""
204 end if
205
206 replaceInstance = out
207
208 oInstanceConfig.RemoveAll
209 oInstanceEvents.RemoveAll
210 end function
211
212 ''
213 ' Replace all &lt;textarea&gt; elements available in the document with editor instances.
214 '
215 ' @param className (string) If set, replace all textareas with class className in the page.
216 '
217 ' Example 1: replace all &lt;textarea&gt; elements in the page.
218 ' @code
219 ' editor = new CKEditor
220 ' editor.replaceAll empty
221 ' @endcode
222 '
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
228 '
229 function replaceAll(className)
230 dim out, js, customConfig
231
232 out = ""
233 if not(initialized) then
234 out = out & init()
235 end if
236
237 set customConfig = configSettings()
238 js = returnGlobalEvents()
239
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
256
257 out = out & script(js)
258
259 if not(returnOutput) then
260 response.write out
261 out = ""
262 end if
263
264 replaceAll = out
265
266 oInstanceConfig.RemoveAll
267 oInstanceEvents.RemoveAll
268 end function
269
270
271 ''
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
274 '
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
284
285 ''
286 ' Configuration options for the next instance
287 '
288 Public Property Let instanceConfig( configKey, configValue )
289 oInstanceConfig.Add configKey, configValue
290 End Property
291
292 ''
293 ' Adds event listener.
294 ' Events are fired by %CKEditor in various situations.
295 '
296 ' @param eventName (string) Event name.
297 ' @param javascriptCode (string) Javascript anonymous function or function name.
298 '
299 ' Example usage:
300 ' @code
301 ' editor.addEventHandler "instanceReady", "function (ev) { " & _
302 ' " alert('Loaded: ' + ev.editor.name); " & _
303 ' "}"
304 ' @endcode
305 '
306 public sub addEventHandler(eventName, javascriptCode)
307 if not(oAllInstancesEvents.Exists( eventName ) ) then
308 oAllInstancesEvents.Add eventName, Array()
309 end if
310
311 dim listeners, size
312 listeners = oAllInstancesEvents( eventName )
313 size = ubound(listeners) + 1
314 redim preserve listeners(size)
315 listeners(size) = javascriptCode
316
317 oAllInstancesEvents( eventName ) = listeners
318 ' '' Avoid duplicates. fixme...
319 ' if (!in_array($javascriptCode, $this->_events[$event])) {
320 ' $this->_events[$event][] = $javascriptCode;
321 ' }
322 end sub
323
324 ''
325 ' Clear registered event handlers.
326 ' Note: this function will have no effect on already created editor instances.
327 '
328 ' @param eventName (string) Event name, if set to 'empty' all event handlers will be removed.
329 '
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
337
338
339 ''
340 ' Adds event listener only for the next instance.
341 ' Events are fired by %CKEditor in various situations.
342 '
343 ' @param eventName (string) Event name.
344 ' @param javascriptCode (string) Javascript anonymous function or function name.
345 '
346 ' Example usage:
347 ' @code
348 ' editor.addInstanceEventHandler "instanceReady", "function (ev) { " & _
349 ' " alert('Loaded: ' + ev.editor.name); " & _
350 ' "}"
351 ' @endcode
352 '
353 public sub addInstanceEventHandler(eventName, javascriptCode)
354 if not(oInstanceEvents.Exists( eventName ) ) then
355 oInstanceEvents.Add eventName, Array()
356 end if
357
358 dim listeners, size
359 listeners = oInstanceEvents( eventName )
360 size = ubound(listeners) + 1
361 redim preserve listeners(size)
362 listeners(size) = javascriptCode
363
364 oInstanceEvents( eventName ) = listeners
365 ' '' Avoid duplicates. fixme...
366 ' if (!in_array($javascriptCode, $this->_events[$event])) {
367 ' $this->_events[$event][] = $javascriptCode;
368 ' }
369 end sub
370
371 ''
372 ' Clear registered event handlers.
373 ' Note: this function will have no effect on already created editor instances.
374 '
375 ' @param eventName (string) Event name, if set to 'empty' all event handlers will be removed.
376 '
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
384
385 ''
386 ' Adds global event listener.
387 '
388 ' @param event (string) Event name.
389 ' @param javascriptCode (string) Javascript anonymous function or function name.
390 '
391 ' Example usage:
392 ' @code
393 ' editor.addGlobalEventHandler "dialogDefinition", "function (ev) { " & _
394 ' " alert('Loading dialog: ' + ev.data.name); " & _
395 ' "}"
396 ' @endcode
397 '
398 public sub addGlobalEventHandler( eventName, javascriptCode)
399 if not(oGlobalEvents.Exists( eventName ) ) then
400 oGlobalEvents.Add eventName, Array()
401 end if
402
403 dim listeners, size
404 listeners = oGlobalEvents( eventName )
405 size = ubound(listeners) + 1
406 redim preserve listeners(size)
407 listeners(size) = javascriptCode
408
409 oGlobalEvents( eventName ) = listeners
410
411 ' // Avoid duplicates.
412 ' if (!in_array($javascriptCode, $this->_globalEvents[$event])) {
413 ' $this->_globalEvents[$event][] = $javascriptCode;
414 ' }
415 end sub
416
417 ''
418 ' Clear registered global event handlers.
419 ' Note: this function will have no effect if the event handler has been already printed/returned.
420 '
421 ' @param eventName (string) Event name, if set to 'empty' all event handlers will be removed .
422 '
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
430
431 ''
432 ' Prints javascript code.
433 '
434 ' @param string js
435 '
436 private function script(js)
437 script = "<script type=""text/javascript"">" & _
438 "//<![CDATA[" & vbcrlf & _
439 js & vbcrlf & _
440 "//]]>" & _
441 "</script>" & vbcrlf
442 end function
443
444 ''
445 ' Returns the configuration array (global and instance specific settings are merged into one array).
446 '
447 ' @param instanceConfig (Dictionary) The specific configurations to apply to editor instance.
448 ' @param instanceEvents (Dictionary) Event listeners for editor instance.
449 '
450 private function configSettings()
451 dim mergedConfig, mergedEvents
452 set mergedConfig = cloneDictionary(oAllInstancesConfig)
453 set mergedEvents = cloneDictionary(oAllInstancesEvents)
454
455 if not(isEmpty(oInstanceConfig)) then
456 set mergedConfig = mergeDictionary(mergedConfig, oInstanceConfig)
457 end if
458
459 if not(isEmpty(oInstanceEvents)) then
460 for each eventName in oInstanceEvents
461 code = oInstanceEvents( eventName )
462
463 if not(mergedEvents.Exists( eventName)) then
464 mergedEvents.Add eventName, code
465 else
466
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
481
482 mergedEvents( eventName ) = listeners
483 end if
484 next
485
486 end if
487
488 dim i, eventName, handlers, configON, ub, code
489
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
497
498 for each eventName in mergedEvents
499 handlers = mergedEvents( eventName )
500 code = ""
501
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
519
520 ' set mergedConfig.Item("on") = configOn
521 end if
522
523 set configSettings = mergedConfig
524 end function
525
526 ''
527 ' Returns a copy of a scripting.dictionary object
528 '
529 private function cloneDictionary( base )
530 dim newOne, tmpKey
531
532 Set newOne = CreateObject("Scripting.Dictionary")
533 for each tmpKey in base
534 newOne.Add tmpKey , base( tmpKey )
535 next
536
537 set cloneDictionary = newOne
538 end function
539
540 ''
541 ' Combines two scripting.dictionary objects
542 ' The base object isn't modified, and extra gets all the properties in base
543 '
544 private function mergeDictionary(base, extra)
545 dim newOne, tmpKey
546
547 for each tmpKey in base
548 if not(extra.Exists( tmpKey )) then
549 extra.Add tmpKey, base( tmpKey )
550 end if
551 next
552
553 set mergeDictionary = extra
554 end function
555
556 ''
557 ' Return global event handlers.
558 '
559 private function returnGlobalEvents()
560 dim out, eventName, handlers
561 dim handlersForEvent, handler, code, i
562 out = ""
563
564 if (isempty(CKEDITOR_returnedEvents)) then
565 set CKEDITOR_returnedEvents = CreateObject("Scripting.Dictionary")
566 end if
567
568 for each eventName in oGlobalEvents
569 handlers = oGlobalEvents( eventName )
570
571 if not(CKEDITOR_returnedEvents.Exists(eventName)) then
572 CKEDITOR_returnedEvents.Add eventName, CreateObject("Scripting.Dictionary")
573 end if
574
575 set handlersForEvent = CKEDITOR_returnedEvents.Item( eventName )
576
577 ' handlersForEvent is another dictionary
578 ' and handlers is an array
579
580 for i = 0 to ubound(handlers)
581 code = handlers( i )
582
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
591
592 returnGlobalEvents = out
593 end function
594
595 ''
596 ' Initializes CKEditor (executed only once).
597 '
598 private function init()
599 dim out, args, path, extraCode, file
600 out = ""
601
602 if (CKEDITOR_initComplete) then
603 init = ""
604 exit function
605 end if
606
607 if (initialized) then
608 CKEDITOR_initComplete = true
609 init = ""
610 exit function
611 end if
612
613 args = ""
614 path = ckeditorPath()
615
616 if (timestamp <> "") and (timestamp <> "%" & "TIMESTAMP%") then
617 args = "?t=" & timestamp
618 end if
619
620 ' Skip relative paths...
621 if (instr(path, "..") <> 0) then
622 out = out & script("window.CKEDITOR_BASEPATH='" & path & "';")
623 end if
624
625 out = out & "<scr" & "ipt type=""text/javascript"" src=""" & path & ckeditorFileName() & args & """></scr" & "ipt>" & vbcrlf
626
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
634
635 CKEDITOR_initComplete = true
636 initialized = true
637
638 init = out
639 end function
640
641 private function ckeditorFileName()
642 ckeditorFileName = "ckeditor.js"
643 end function
644
645 ''
646 ' Return path to ckeditor.js.
647 '
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
655
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
659
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
665
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
675
676 End Class
677
678
679
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}
697 ''
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
707
708 '**************************************************************************************************************
709 class JSON
710
711 'private members
712 private output, innerCall
713
714 '**********************************************************************************************************
715 '* constructor
716 '**********************************************************************************************************
717 public sub class_initialize()
718 newGeneration()
719 end sub
720
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
753
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
802
803 if innerCall = 0 then newGeneration()
804 end function
805
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
861
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
877
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
898
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
916
917 '******************************************************************************************************************
918 '* newGeneration
919 '******************************************************************************************************************
920 private sub newGeneration()
921 output = empty
922 innerCall = 0
923 end sub
924
925 '******************************************************************************************
926 '* JsonEscapeSquence
927 '******************************************************************************************
928 private function escapequence(digit)
929 escapequence = "\u00" + right(padLeft(hex(ascw(digit)), 2, 0), 2)
930 end function
931
932 '******************************************************************************************
933 '* padLeft
934 '******************************************************************************************
935 private function padLeft(value, totalLength, paddingChar)
936 padLeft = right(clone(paddingChar, totalLength) & value, totalLength)
937 end function
938
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
946
947 '******************************************************************************************
948 '* write
949 '******************************************************************************************
950 private sub write(val)
951 output = output & val
952 end sub
953
954 end class
955 %>