forked from singularityhacker/FiddlePlus
-
Notifications
You must be signed in to change notification settings - Fork 0
/
script.test.js
624 lines (601 loc) · 24.2 KB
/
script.test.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
/**
* Copyright (c) 2010 Mike Kent
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
(function($) {
/*
* Calling conventions:
*
* $.zc( ZenCode | ZenObject [, data] )
*
* ZenCode: string to be parsed into HTML
* ZenObject: Collection of ZenCode and ZenObjects. ZenObject.main must
* be defined
*/
$.zc = $.zen = function(ZenCode,data) {
if(data !== undefined)
var functions = data.functions;
var el = createHTMLBlock(ZenCode,data,functions);
return el;
};
var regZenTagDfn =
/*
* (
* [#\.@]?[\w!-]+ # tag names, ids, classes, and references
* |
* \[ # attributes
* ([(\w|\-)!?=:"']+ # attribute name
* (="([^"]|\\")+")? # attribute value
* {0,})+ # allow spaces, and look for 1+ attributes
* \]
* |
* \~[\w$]+=[\w$]+ # events in form -event=function
* |
* &[\w$\+(=[\w$]+)? # data in form &data[=variable]
* |
* [#\.\@]? # allow for types to precede dynamic names
* !([^!]|\\!)+!){0,} # contents enclosed by !...!
* |
* (?:[^\\]|^) # find either \ or beginning of line
* !
* ){0,} # 0 or more of the above
* (\{ # contents
* (
* [^\}]
* |
* \\\} # find all before }, but include \}
* )+
* \})?
*/
/([#\.\@\*]?[\w-]+|\[([(\w|\-)!?=:"']+(="([^"]|\\")+")? {0,})+\]|\~[\w$]+=[\w$]+|&[\w$]+(=[\w$]+)?|[#\.\@]?!([^!]|\\!)+!){0,}(\{([^\}]|\\\})+\})?/i,
regTag = /(\w+)/i, //finds only the first word, must check for now word
regId = /[^\[]#((\-|[\w])+)/i, //finds id name
regTagNotContent = /((([#\.]?[\w-]+)?(\[([\w!]+(="([^"]|\\")+")? {0,})+\])?)+)/i,
regClasses = /(\.[\w-]+)/gi, //finds all classes
regClass = /\.([\w-]+)/i, //finds the class name of each class
//finds reference objects
regReference = /(@[\w$_][\w$_\d]+)/i,
//finds attributes within '[' and ']' of type name or name="value"
regRemoveAttrs = /([\[].*[\]])/gi, //matches brackets and everything within
regAttrDfn = /(\[([(\w|\-)!]+(="([^"]|\\")+")? {0,})+\])/i,
regAttrs = /([(\w|\-)!]+(="([^"]|\\")+")?)/gi, //finds each attribute
regAttr = /([(\w|\-)!]+)(="(([^"]|\\")+)")?/i, //finds individual attribute and value
//finds content within '{' and '}' while ignoring '\}'
regCBrace = /\{(([^\}]|\\\})+)\}/i,
//finds content within !...! while ignoring '\!' within !...!
regExclamation = /(?:([^\\]|^))!([^!]|\\!)+!/gim,
//finds events in form of -event=function
regEvents = /\~[\w$]+(=[\w$]+)?/gi,
regEvent = /\~([\w$]+)=([\w$]+)/i,
//find data in form &data or &dataname=data
regDatas = /&[\w$]+(=[\w$]+)?/gi,
regData = /&([\w$]+)(=([\w$]+))?/i;
/*
* The magic happens here.
*
* This is the recursive function to break up, parse, and create every
* element.
*/
function createHTMLBlock(ZenObject,data,functions,indexes) {
if($.isPlainObject(ZenObject))
var ZenCode = ZenObject.main;
else {
var ZenCode = ZenObject;
ZenObject = {
main: ZenCode
};
}
var origZenCode = ZenCode;
if(indexes === undefined)
indexes = {};
// Take care of !for:...! and !if:...! structure and if $.isArray(data)
if(ZenCode.charAt(0)=='!' || $.isArray(data)) {
// If data is simply an array, then handle loop specially.
// This allows for some shorthand and quick templating.
if($.isArray(data))
var forScope = ZenCode;
// Check to see if an index is specified
else {
var obj = parseEnclosure(ZenCode,'!');
obj = obj.substring(obj.indexOf(':')+1,obj.length-1);
var forScope = parseVariableScope(ZenCode);
}
// Only parse the scope of the !for:! after taking care of references
while(forScope.charAt(0) == '@')
forScope = parseVariableScope(
'!for:!'+parseReferences(forScope, ZenObject));
// setup a zen object with the forScope as main
var zo = ZenObject;
zo.main = forScope;
// initialize el for consistent use
var el = $();
if(ZenCode.substring(0,5)=="!for:" || $.isArray(data)) { //!for:...!
// again, data as an array is handled differently
if(!$.isArray(data) && obj.indexOf(':')>0) {
var indexName = obj.substring(0,obj.indexOf(':'));
obj = obj.substr(obj.indexOf(':')+1);
}
// setup the array to either be data as a whole or an object
// within data. This is the reason for the two special exceptions
// above to handle data as an aray.
var arr = $.isArray(data)?data:data[obj];
var zc = zo.main;
if($.isArray(arr) || $.isPlainObject(arr)) {
$.map(arr, function(value, index) {
zo.main = zc;
// initialize index if it was specified
if(indexName!==undefined)
indexes[indexName] = index;
// allow for array references as "value" by wrapping the array
// element.
if(!$.isPlainObject(value))
value = {value:value};
// create the element based on ZenObject previously created
var next = createHTMLBlock(zo,value,functions,indexes);
if(el.length == 0)
el = next;
// append elements... TODO: is this "if" necessary?
else {
$.each(next, function(index,value) {
el.push(value);
});
}
});
}
// if data is an array, then the whole ZenCode is looped, therefore
// there is nothing left to do.
if(!$.isArray(data))
ZenCode = ZenCode.substr(obj.length+6+forScope.length);
else
ZenCode = '';
} else if(ZenCode.substring(0,4)=="!if:") { //!if:...!
// check result of if contents
var result = parseContents('!'+obj+'!',data,indexes);
// Only execute ZenCode if the result was positive.
if(result!='undefined' || result!='false' || result!='')
el = createHTMLBlock(zo,data,functions,indexes);
ZenCode = ZenCode.substr(obj.length+5+forScope.length);
}
// setup function ZenObject.main to reflect changes in both !for:!
// and !if:!
ZenObject.main = ZenCode;
}
// Take care of nested groups
else if(ZenCode.charAt(0)=='(') {
// get full parenthetical group
var paren = parseEnclosure(ZenCode,'(',')');
// exclude beginning and ending parentheses
var inner = paren.substring(1,paren.length-1);
// update ZenCode for later
ZenCode = ZenCode.substr(paren.length);
var zo = ZenObject;
zo.main = inner;
// create Element(s) based on contents of group
var el = createHTMLBlock(zo,data,functions,indexes);
}
// Everything left should be a regular block
else {
var blocks = ZenCode.match(regZenTagDfn);
var block = blocks[0]; // actual block to create
if(block.length == 0) {
return '';
}
// dereference references if any
// references can drastically change the code in unexpected ways
// so it is required to reparse the whole ZenObject.
if(block.indexOf('@') >= 0) {
ZenCode = parseReferences(ZenCode,ZenObject);
var zo = ZenObject;
zo.main = ZenCode;
return createHTMLBlock(zo,data,functions,indexes);
}
//if there are multiples of an element
if(block.indexOf('*') > 0){
var numElements = block.split('*')[1];
}else{
//only 1
var numElements = 1;
}
// apply any dynamic content to block ZenCode
block = parseContents(block,data,indexes);
// get all classes
var blockClasses = parseClasses(block);
// get block id if any
if(regId.test(block))
var blockId = regId.exec(block)[1];
// get block attributes
var blockAttrs = parseAttributes(block,data);
// default block tag is div unless block is only {...}, thenspan
var blockTag = block.charAt(0)=='{'?'span':'div';
// get block tag if it is explicitly defined
if(ZenCode.charAt(0)!='#' && ZenCode.charAt(0)!='.' &&
ZenCode.charAt(0)!='{')
blockTag = regTag.exec(block)[1];
// get block HTML contents
if(block.search(regCBrace) != -1)
var blockHTML = block.match(regCBrace)[1];
// create jQuery attribute object with all data
blockAttrs = $.extend(blockAttrs, {
id: blockId,
'class': blockClasses,
html: blockHTML
});
// create Element based on block
var blockString = '';
for(i=numElements; i>0; i--){
blockString += '<' + blockTag + '>';
}
var el = $(blockString, blockAttrs);
el.attr(blockAttrs); //fixes IE error (issue 2)
// bind created element with any events and data
el = bindEvents(block, el, functions);
el = bindData(block, el, data);
// remove block from ZenCode and update ZenObject
ZenCode = ZenCode.substr(blocks[0].length);
ZenObject.main = ZenCode;
}
// Recurse based on '+', '*' or '>'
if(ZenCode.length > 0) {
// Create children
if(ZenCode.charAt(0) == '>') {
// one or more elements enclosed in a group
if(ZenCode.charAt(1) == '(') {
var zc = parseEnclosure(ZenCode.substr(1),'(',')');
ZenCode = ZenCode.substr(zc.length+1);
}
// dynamically created elements or !for:! or !if:!
else if(ZenCode.charAt(1) == '!') {
var obj = parseEnclosure(ZenCode.substr(1),'!');
var forScope = parseVariableScope(ZenCode.substr(1));
var zc = obj+forScope;
ZenCode = ZenCode.substr(zc.length+1);
}
// a single element that either ends the ZenCode or has siblings
else {
var len = Math.max(ZenCode.indexOf('+'),ZenCode.length);
var zc = ZenCode.substring(1, len);
ZenCode = ZenCode.substr(len);
}
var zo = ZenObject;
zo.main = zc;
// recurse and append
var els = $(
createHTMLBlock(zo,data,functions,indexes)
);
els.appendTo(el);
}
// Create siblings
if(ZenCode.charAt(0) == '+') {
var zo = ZenObject;
zo.main = ZenCode.substr(1);
// recurse and push new elements with current ones
var el2 = createHTMLBlock(zo,data,functions,indexes);
$.each(el2, function(index,value) {
el.push(value);
});
}
}
var ret = el;
return ret;
}
/*
* Binds the appropiate data to the element specified by
* &data=value
* Or in the case of
* &data
* binds data.data to data on the element.
*/
function bindData(ZenCode, el, data) {
if(ZenCode.search(regDatas) == 0)
return el;
var datas = ZenCode.match(regDatas);
if(datas === null)
return el;
for(var i=0;i<datas.length;i++) {
var split = regData.exec(datas[i]);
// the data dfn can be either &dfn or &data=dfn
if(split[3] === undefined)
$(el).data(split[1],data[split[1]]);
else
$(el).data(split[1],data[split[3]]);
}
return el;
}
/*
* Binds the appropiate function to the event specified by
* ~event=function
* Or in the case of
* ~event
* binds function.event to event.
*/
function bindEvents(ZenCode, el, functions) {
if(ZenCode.search(regEvents) == 0)
return el;
var bindings = ZenCode.match(regEvents);
if(bindings === null)
return el;
for(var i=0;i<bindings.length;i++) {
var split = regEvent.exec(bindings[i]);
// function dfn can be either ~dfn or ~function=dfn
if(split[2] === undefined)
var fn = functions[split[1]];
else
var fn = functions[split[2]];
$(el).bind(split[1],fn);
}
return el;
}
/*
* parses attributes out of a single css element definition
* returns as a space delimited string of attributes and their values
*/
function parseAttributes(ZenBlock, data) {
if(ZenBlock.search(regAttrDfn) == -1)
return undefined;
var attrStrs = ZenBlock.match(regAttrDfn);
attrStrs = attrStrs[0].match(regAttrs);
var attrs = {};
for(var i=0;i<attrStrs.length;i++) {
var parts = regAttr.exec(attrStrs[i]);
attrs[parts[1]] = '';
// all attributes must be attr="value"
if(parts[3] !== undefined)
attrs[parts[1]] = parseContents(parts[3],data);
}
return attrs;
}
/*
* parses classes out of a single css element definition
* returns as a space delimited string of classes
*/
function removeDigit(zenText){
while(/[0-9].*/.exec(zenText)){
zenText = zenText.substr(1);
}
return zenText;
}
function parseClasses(ZenBlock) {
//strip attributes from string
//Would prefer to use regex here, but cannot get a working one
if(ZenBlock.indexOf('[')){
ZenBlock=ZenBlock.split('[')[0];
}
if(ZenBlock.indexOf('*') > 0){
var zenString = '';
var tempZenBlock = ZenBlock.split('*');
for(i=0; i<tempZenBlock.length; i++){
zenString += removeDigit(tempZenBlock[i]);
}
ZenBlock = zenString;
}
ZenBlock = ZenBlock.match(regTagNotContent)[0];
if(ZenBlock.search(regClasses) == -1)
return undefined;
var classes = ZenBlock.match(regClasses);
var clsString = '';
for(var i=0;i<classes.length;i++) {
clsString += ' '+regClass.exec(classes[i])[1];
}
return $.trim(clsString);
}
/*
* Converts !...! into its javascript equivelant.
*/
function parseContents(ZenBlock, data, indexes) {
if(indexes===undefined)
indexes = {};
var html = ZenBlock;
if(data===undefined)
return html;
//The while takes care of the issue .!fruit!!fruit=="bla"?:".sd":""!
//aka contigous !...!
while(regExclamation.test(html)) {
html = html.replace(regExclamation, function(str, str2) {
var begChar = '';
// don't process !for:! or !if:!
if(str.indexOf("!for:") > 0 || str.indexOf("!if:") > 0)
return str;
// regex can return either !val! or x!val! where x is a misc char
// begChar takes care of this second possability and saves the
// character to be restored back to the string
if(str.charAt(0) == '!')
str = str.substring(1,str.length-1);
else {
begChar = str.charAt(0);
str = str.substring(2,str.length-1);
}
// wrap a function with dfn to find value in either data or indexes
var fn = new Function('data','indexes',
'var r=undefined;'+
'with(data){try{r='+str+';}catch(e){}}'+
'with(indexes){try{if(r===undefined)r='+str+';}catch(e){}}'+
'return r;');
var val = unescape(fn(data,indexes));
//var val = fn(data,indexes);
return begChar+val;
});
}
html = html.replace(/\\./g,function (str) {
return str.charAt(1);
});
return unescape(html);
}
/*
* There are actually three forms of this function:
*
* parseEnclosure(ZenCode,open) - use open as both open and close
* parseEnclosure(ZenCode,open,close) - specify both
* parseEnclosure(ZenCode,open,close,count) - specify initial count
*/
function parseEnclosure(ZenCode,open,close,count) {
if(close===undefined)
close = open;
var index = 1;
// allow count to be either 1 if the string starts with an open char
// or 0 and then return if it does not.
if(count === undefined)
count = ZenCode.charAt(0)==open?1:0;
if(count==0)
return;
// go through each character to find the end of the enclosure while
// keeping track of how deeply nested the parser currently is
// and ignoring escaped enclosure characters.
for(;count>0 && index<ZenCode.length;index++) {
if(ZenCode.charAt(index)==close && ZenCode.charAt(index-1)!='\\')
count--;
else if(ZenCode.charAt(index)==open && ZenCode.charAt(index-1)!='\\')
count++;
}
var ret = ZenCode.substring(0,index);
return ret;
}
/*
* Parses multiple ZenCode references. The initial ZenCode must be
* declared as ZenObject.main
*/
function parseReferences(ZenCode, ZenObject) {
ZenCode = ZenCode.replace(regReference, function(str) {
str = str.substr(1);
// wrap str in a function to find its value in the ZenObject
var fn = new Function('objs',//'reparse',
'var r="";'+
'with(objs){try{'+
//'if($.isPlainObject('+str+'))'+
// 'r=reparse('+str+');'+
//'else '+
'r='+str+';'+
'}catch(e){}}'+
'return r;');
return fn(ZenObject,parseReferences);
});
return ZenCode;
}
/*
* Parses the scope of a !for:...!
*
* The scope of !for:...! is:
* If the tag has no children, then only immeiately following tag
* Tag and its children
*/
function parseVariableScope(ZenCode) {
// only parse !for:! or !if:!
if(ZenCode.substring(0,5)!="!for:" &&
ZenCode.substring(0,4)!="!if:")
return undefined;
// find the enclosure and remove it from the string
var forCode = parseEnclosure(ZenCode,'!');
ZenCode = ZenCode.substr(forCode.length);
// scope of !for:! and !if:! can only be one (if any) group of elements
if(ZenCode.charAt(0) == '(') {
return parseEnclosure(ZenCode,'(',')');
}
var tag = ZenCode.match(regZenTagDfn)[0];
ZenCode = ZenCode.substr(tag.length);
// scope of !for:! and !if:! is the single element and its children
if(ZenCode.length==0 || ZenCode.charAt(0)=='+') {
return tag;
}
else if(ZenCode.charAt(0)=='>') {
var rest = '';
rest = parseEnclosure(ZenCode.substr(1),'(',')',1);
return tag+'>'+rest;
}
return undefined;
}
})(jQuery);
var zenFunctionality = function(){
var textarea, zen;
};
zenFunctionality.prototype.createTextArea = function(id){
var textArea = document.createElement("textarea");
textArea.id = 'zenText';
textArea.style.display = 'none';
}
zenFunctionality.prototype.updateTextArea = function(txtArea, string){
txtArea.value = string;
}
zenFunctionality.prototype.getCaretPosition = function(element, zenSet) {
//var range= window.getSelection().getRangeAt(0);
var cursorPosition = $('#test').prop("selectionStart");
//alert('Current position: '+range.startOffset+' inside '+range.startContainer);
alert(cursorPosition);
return cursorPosition;
}
function showCaretPos() {
var el = document.getElementById("test");
var caretPosEl = document.getElementById("caretPos");
caretPosEl.innerHTML = "Caret position: " + getCaretCharacterOffsetWithin(el);
}
zenFunctionality.prototype.returnWord = function(text, caretPos) {
var index = text.indexOf(caretPos);
var preText = text.substring(0, caretPos);
if (preText.indexOf(" ") > 0) {
var words = preText.split(" ");
return words[words.length - 1]; //return last word
}
else {
return preText;
}
}
zenFunctionality.prototype.convertPrevWord = function(e) {
var editableDiv = e.target;
var caretPos = this.getCaretPosition(editableDiv);
var word = this.returnWord(editableDiv.value, caretPos);
if (word != null) {
HTMlString=$.zc(word);
console.log(HTMlString);
if(HTMlString.length < 1){
return HTMlString[0].outerHTML;
}else{
var returnString = '';
for(i=0;i<HTMlString.length; i++){
returnString += HTMlString[i].outerHTML;
}
return returnString;
}
}
}
$(function() {
// Adds script to body. Can be seen via inspector
/*var script = document.createElement("script");
script.type = "text/javascript";
script.src = "zen_textarea.min.js";
//document.body.appendChild(script);
*/
var htmlEdit = new zenFunctionality();
console.log(htmlEdit);
//var baseExtensionURL = chrome.extension.getURL("js/");
var isCtrl = false;
$('#test').keyup(function (e) {
e.preventDefault();
if(e.which == 17) isCtrl=false;
}).keydown(function (e) {
if(e.which == 17) isCtrl=true;
if(e.which == 69 && isCtrl == true) {
var zenSet="html";
htmString = htmlEdit.convertPrevWord(e, zenSet);
document.getElementById('test').value = htmString;
isCtrl = false;
e.preventDefault();
return false;
}
});
});