mizugumo 0.1.4 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,1172 +0,0 @@
1
- /*
2
- * NinjaScript - 0.8.3
3
- * written by and copyright 2010-2011 Judson Lester and Logical Reality Design
4
- * Licensed under the MIT license
5
- * 2011-02-04
6
- *
7
- * Those new to this source should skim down to standardBehaviors
8
- */
9
- Ninja = (function() {
10
- function log(message) {
11
- if(false) { //LOGGING TURNED OFF IS 100% faster!
12
- try {
13
- console.log(message)
14
- }
15
- catch(e) {} //we're in IE or FF w/o Firebug or something
16
- }
17
- }
18
-
19
- function isArray(candidate) {
20
- return (candidate.constructor == Array)
21
- }
22
-
23
- function forEach(list, callback, thisArg) {
24
- if(typeof list.forEach == "function") {
25
- return list.forEach(callback, thisArg)
26
- }
27
- else if(typeof Array.prototype.forEach == "function") {
28
- return Array.prototype.forEach.call(list, callback, thisArg)
29
- }
30
- else {
31
- var len = Number(list.length)
32
- for(var k = 0; k < len; k+=1) {
33
- if(typeof list[k] != "undefined") {
34
- callback.call(thisArg, list[k], k, list)
35
- }
36
- }
37
- return
38
- }
39
- }
40
-
41
- function NinjaScript() {
42
- //NinjaScript-wide configurations. Currently, not very many
43
- this.config = {
44
- //This is the half-assed: it should be template of some sort
45
- messageWrapping: function(text, classes) {
46
- return "<div class='flash " + classes +"'><p>" + text + "</p></div>"
47
- },
48
- messageList: "#messages",
49
- busyLaziness: 200
50
- }
51
-
52
-
53
- this.behavior = this.goodBehavior
54
- this.tools = new Tools(this)
55
- }
56
-
57
- NinjaScript.prototype = {
58
-
59
- packageBehaviors: function(callback) {
60
- var types = {
61
- does: Behavior,
62
- chooses: Metabehavior,
63
- selects: Selectabehavior
64
- }
65
- result = callback(types)
66
- this.tools.enrich(this, result)
67
- },
68
-
69
- goodBehavior: function(dispatching) {
70
- var collection = this.tools.getRootCollection()
71
- for(var selector in dispatching)
72
- {
73
- if(typeof dispatching[selector] == "undefined") {
74
- log("Selector " + selector + " not properly defined - ignoring")
75
- }
76
- else {
77
- collection.addBehavior(selector, dispatching[selector])
78
- }
79
- }
80
- jQuery(window).load( function(){ Ninja.go() } )
81
- },
82
-
83
- badBehavior: function(nonsense) {
84
- throw new Error("Called Ninja.behavior() after Ninja.go() - don't do that. 'Go' means 'I'm done, please proceed'")
85
- },
86
-
87
- go: function() {
88
- if(this.behavior != this.misbehavior) {
89
- var rootOfDocument = this.tools.getRootOfDocument()
90
- rootOfDocument.bind("DOMSubtreeModified DOMNodeInserted thisChangedDOM", handleMutation);
91
- //If we ever receive either of the W3C DOMMutation events, we don't need our IE based
92
- //hack, so nerf it
93
- rootOfDocument.one("DOMSubtreeModified DOMNodeInserted", function(){
94
- Ninja.tools.detachSyntheticMutationEvents()
95
- })
96
- this.behavior = this.badBehavior
97
- this.tools.fireMutationEvent()
98
- }
99
- }
100
- }
101
-
102
-
103
- function Tools(ninja) {
104
- this.ninja = ninja
105
- }
106
-
107
- Tools.prototype = {
108
- //Handy JS things
109
- forEach: forEach,
110
- enrich: function(left, right) {
111
- return jQuery.extend(left, right)
112
- },
113
- ensureDefaults: function(config, defaults) {
114
- if(!config instanceof Object) {
115
- config = {}
116
- }
117
- return this.enrich(defaults, config)
118
- },
119
- //DOM and Events
120
- getRootOfDocument: function() {
121
- return jQuery("html") //document.firstChild)
122
- },
123
- clearRootCollection: function() {
124
- Ninja.behavior = Ninja.goodBehavior
125
- this.getRootOfDocument().data("ninja-behavior", null)
126
- },
127
- getRootCollection: function() {
128
- var rootOfDocument = this.getRootOfDocument()
129
- if(rootOfDocument.data("ninja-behavior") instanceof BehaviorCollection) {
130
- return rootOfDocument.data("ninja-behavior")
131
- }
132
-
133
- var collection = new BehaviorCollection()
134
- rootOfDocument.data("ninja-behavior", collection);
135
- return collection
136
- },
137
- suppressChangeEvents: function() {
138
- return new Behavior({
139
- events: {
140
- DOMSubtreeModified: function(e){},
141
- DOMNodeInserted: function(e){}
142
- }
143
- })
144
- },
145
- addMutationTargets: function(targets) {
146
- this.getRootCollection().addMutationTargets(targets)
147
- },
148
- fireMutationEvent: function() {
149
- this.getRootCollection().fireMutationEvent()
150
- },
151
- detachSyntheticMutationEvents: function() {
152
- this.getRootCollection().fireMutationEvent = function(){}
153
- this.getRootCollection().addMutationTargets = function(t){}
154
- },
155
- //HTML Utils
156
- copyAttributes: function(from, to, which) {
157
- var attributeList = []
158
- var attrs = []
159
- var match = new RegExp("^" + which.join("$|^") + "$")
160
- to = jQuery(to)
161
- this.forEach(from.attributes, function(att) {
162
- if(match.test(att.nodeName)) {
163
- to.attr(att.nodeName, att.nodeValue)
164
- }
165
- })
166
- },
167
- deriveElementsFrom: function(element, means){
168
- switch(typeof means){
169
- case 'undefined': return element
170
- case 'string': return jQuery(means)
171
- case 'function': return means(element)
172
- }
173
- },
174
- extractMethod: function(element, formData) {
175
- if(element.dataset !== undefined &&
176
- element.dataset["method"] !== undefined &&
177
- element.dataset["method"].length > 0) {
178
- log("Override via dataset: " + element.dataset["method"])
179
- return element.dataset["method"]
180
- }
181
- if(element.dataset === undefined &&
182
- jQuery(element).attr("data-method") !== undefined) {
183
- log("Override via data-method: " + jQuery(element).attr("data-method"))
184
- return jQuery(element).attr("data-method")
185
- }
186
- if(typeof formData !== "undefined") {
187
- for(var i=0, len = formData.length; i<len; i++) {
188
- if(formData[i].name == "Method") {
189
- log("Override via Method: " + formData[i].value)
190
- return formData[i].value
191
- }
192
- }
193
- }
194
- if(typeof element.method !== "undefined") {
195
- return element.method
196
- }
197
- return "GET"
198
- },
199
- //Ninjascript utils
200
- cantTransform: function() {
201
- throw new TransformFailedException
202
- },
203
- applyBehaviors: function(element, behaviors) {
204
- this.getRootCollection().apply(element, behaviors)
205
- },
206
- message: function(text, classes) {
207
- var addingMessage = this.ninja.config.messageWrapping(text, classes)
208
- jQuery(this.ninja.config.messageList).append(addingMessage)
209
- },
210
- hiddenDiv: function() {
211
- var existing = jQuery("div#ninja-hide")
212
- if(existing.length > 0) {
213
- return existing[0]
214
- }
215
-
216
- var hide = jQuery("<div id='ninja-hide'></div>").css("display", "none")
217
- jQuery("body").append(hide)
218
- Ninja.tools.getRootCollection().applyBehaviorsTo(hide, [Ninja.tools.suppressChangeEvents()])
219
- return hide
220
- },
221
- ajaxSubmitter: function(form) {
222
- return new AjaxSubmitter(form)
223
- },
224
- overlay: function() {
225
- // I really liked using
226
- //return new Overlay([].map.apply(arguments,[function(i) {return i}]))
227
- //but IE8 doesn't implement ECMA 2.6.2 5th ed.
228
-
229
- return new Overlay(jQuery.makeArray(arguments))
230
- },
231
- busyOverlay: function(elem) {
232
- var overlay = this.overlay(elem)
233
- overlay.set.addClass("ninja_busy")
234
- overlay.laziness = this.ninja.config.busyLaziness
235
- return overlay
236
- },
237
- //Currently, this doesn't respect changes to the original block...
238
- //There should be an "Overlay behavior" that gets applied
239
- buildOverlayFor: function(elem) {
240
- var overlay = jQuery(document.createElement("div"))
241
- var hideMe = jQuery(elem)
242
- var offset = hideMe.offset()
243
- overlay.css("position", "absolute")
244
- overlay.css("top", offset.top)
245
- overlay.css("left", offset.left)
246
- overlay.width(hideMe.outerWidth())
247
- overlay.height(hideMe.outerHeight())
248
- overlay.css("zIndex", "2")
249
- return overlay
250
- }
251
- }
252
-
253
- var Ninja = new NinjaScript();
254
- //Below here is the dojo - the engines that make NinjaScript work.
255
- //With any luck, only the helpful and curious should have call to keep
256
- //reading
257
- //
258
-
259
- function handleMutation(evnt) {
260
- Ninja.tools.getRootCollection().mutationEventTriggered(evnt);
261
- }
262
-
263
- function AjaxSubmitter() {
264
- this.formData = []
265
- this.action = "/"
266
- this.method = "GET"
267
- this.dataType = 'script'
268
-
269
- return this
270
- }
271
-
272
- AjaxSubmitter.prototype = {
273
- submit: function() {
274
- log("Computed method: " + this.method)
275
- jQuery.ajax(this.ajaxData())
276
- },
277
-
278
- ajaxData: function() {
279
- return {
280
- data: this.formData,
281
- dataType: this.dataType,
282
- url: this.action,
283
- type: this.method,
284
- complete: this.responseHandler(),
285
- success: this.successHandler(),
286
- error: this.onError
287
- }
288
- },
289
-
290
- successHandler: function() {
291
- var submitter = this
292
- return function(data, statusTxt, xhr) {
293
- submitter.onSuccess(xhr, statusTxt, data)
294
- }
295
- },
296
- responseHandler: function() {
297
- var submitter = this
298
- return function(xhr, statusTxt) {
299
- submitter.onResponse(xhr, statusTxt)
300
- Ninja.tools.fireMutationEvent()
301
- }
302
- },
303
-
304
- onResponse: function(xhr, statusTxt) {
305
- },
306
- onSuccess: function(xhr, statusTxt, data) {
307
- },
308
- onError: function(xhr, statusTxt, errorThrown) {
309
- log(xhr.responseText)
310
- Ninja.tools.message("Server error: " + xhr.statusText, "error")
311
- }
312
- }
313
-
314
- function Overlay(list) {
315
- var elements = this.convertToElementArray(list)
316
- this.laziness = 0
317
- var ov = this
318
- this.set = jQuery(jQuery.map(elements, function(element, idx) {
319
- return ov.buildOverlayFor(element)
320
- }))
321
- }
322
-
323
- Overlay.prototype = {
324
- convertToElementArray: function(list) {
325
- var h = this
326
- switch(typeof list) {
327
- case 'undefined': return []
328
- case 'boolean': return []
329
- case 'string': return h.convertToElementArray(jQuery(list))
330
- case 'function': return h.convertToElementArray(list())
331
- case 'object': {
332
- //IE8 barfs on 'list instanceof Element'
333
- if("focus" in list && "blur" in list && !("jquery" in list)) {
334
- return [list]
335
- }
336
- else if("length" in list && "0" in list) {
337
- var result = []
338
- forEach(list, function(element) {
339
- result = result.concat(h.convertToElementArray(element))
340
- })
341
- return result
342
- }
343
- else {
344
- return []
345
- }
346
- }
347
- }
348
- },
349
-
350
- buildOverlayFor: function(elem) {
351
- var overlay = jQuery(document.createElement("div"))
352
- var hideMe = jQuery(elem)
353
- var offset = hideMe.offset()
354
- overlay.css("position", "absolute")
355
- overlay.css("top", offset.top)
356
- overlay.css("left", offset.left)
357
- overlay.width(hideMe.outerWidth())
358
- overlay.height(hideMe.outerHeight())
359
- overlay.css("zIndex", "2")
360
- overlay.css("display", "none")
361
- return overlay[0]
362
- },
363
- affix: function() {
364
- this.set.appendTo(jQuery("body"))
365
- overlaySet = this.set
366
- window.setTimeout(function() {
367
- overlaySet.css("display", "block")
368
- }, this.laziness)
369
- },
370
- remove: function() {
371
- this.set.remove()
372
- }
373
- }
374
-
375
- function EventScribe() {
376
- this.handlers = {}
377
- this.currentElement = null
378
- }
379
-
380
- EventScribe.prototype = {
381
- makeHandlersRemove: function(element) {
382
- for(var eventName in this.handlers) {
383
- var handler = this.handlers[eventName]
384
- this.handlers[eventName] = function(eventRecord) {
385
- handler.call(this, eventRecord)
386
- jQuery(element).remove()
387
- }
388
- }
389
- },
390
- recordEventHandlers: function (context, behavior) {
391
- if(this.currentElement !== context.element) {
392
- if(this.currentElement !== null) {
393
- this.makeHandlersRemove(this.currentElement)
394
- this.applyEventHandlers(this.currentElement)
395
- this.handlers = {}
396
- }
397
- this.currentElement = context.element
398
- }
399
- for(var eventName in behavior.eventHandlers) {
400
- var oldHandler = this.handlers[eventName]
401
- if(typeof oldHandler == "undefined") {
402
- oldHandler = function(){return true}
403
- }
404
- this.handlers[eventName] = behavior.buildHandler(context, eventName, oldHandler)
405
- }
406
- },
407
- applyEventHandlers: function(element) {
408
- for(var eventName in this.handlers) {
409
- jQuery(element).bind(eventName, this.handlers[eventName])
410
- }
411
- }
412
- }
413
-
414
- function TransformFailedException(){}
415
- function CouldntChooseException() { }
416
-
417
- function RootContext() {
418
- this.stashedElements = []
419
- this.eventHandlerSet = {}
420
- }
421
-
422
- RootContext.prototype = Ninja.tools.enrich(
423
- new Tools(Ninja),
424
- {
425
- stash: function(element) {
426
- this.stashedElements.unshift(element)
427
- },
428
- clearStash: function() {
429
- this.stashedElements = []
430
- },
431
- //XXX Of concern: how do cascading events work out?
432
- //Should there be a first catch? Or a "doesn't cascade" or something?
433
- cascadeEvent: function(event) {
434
- var formDiv = Ninja.tools.hiddenDiv()
435
- forEach(this.stashedElements, function(element) {
436
- var elem = jQuery(element)
437
- elem.data("ninja-visited", this)
438
- jQuery(formDiv).append(elem)
439
- elem.trigger(event)
440
- })
441
- },
442
- unbindHandlers: function() {
443
- var el = jQuery(this.element)
444
- for(eventName in this.eventHandlerSet) {
445
- el.unbind(eventName, this.eventHandlerSet[eventName])
446
- }
447
- }
448
- })
449
-
450
- function BehaviorCollection() {
451
- this.lexicalCount = 0
452
- this.eventQueue = []
453
- this.behaviors = {}
454
- this.selectors = []
455
- this.mutationTargets = []
456
- return this
457
- }
458
-
459
- BehaviorCollection.prototype = {
460
- //XXX: check if this is source of new slowdown
461
- addBehavior: function(selector, behavior) {
462
- if(isArray(behavior)) {
463
- forEach(behavior, function(behaves){
464
- this.addBehavior(selector, behaves)
465
- }, this)
466
- }
467
- else if(behavior instanceof Behavior) {
468
- this.insertBehavior(selector, behavior)
469
- }
470
- else if(behavior instanceof Selectabehavior) {
471
- this.insertBehavior(selector, behavior)
472
- }
473
- else if(behavior instanceof Metabehavior) {
474
- this.insertBehavior(selector, behavior)
475
- }
476
- else if(typeof behavior == "function"){
477
- this.addBehavior(selector, behavior())
478
- }
479
- else {
480
- var behavior = new Behavior(behavior)
481
- this.addBehavior(selector, behavior)
482
- }
483
- },
484
- insertBehavior: function(selector, behavior) {
485
- behavior.lexicalOrder = this.lexicalCount
486
- this.lexicalCount += 1
487
- if(this.behaviors[selector] === undefined) {
488
- this.selectors.push(selector)
489
- this.behaviors[selector] = [behavior]
490
- }
491
- else {
492
- this.behaviors[selector].push(behavior)
493
- }
494
- },
495
- addMutationTargets: function(targets) {
496
- this.mutationTargets = this.mutationTargets.concat(target)
497
- },
498
- fireMutationEvent: function() {
499
- var targets = this.mutationTargets
500
- if (targets.length > 0 ) {
501
- for(var target = targets.shift();
502
- targets.length > 0;
503
- target = targets.shift()) {
504
- jQuery(target).trigger("thisChangedDOM")
505
- }
506
- }
507
- else {
508
- Ninja.tools.getRootOfDocument().trigger("thisChangedDOM")
509
- }
510
- },
511
- mutationEventTriggered: function(evnt){
512
- if(this.eventQueue.length == 0){
513
- log("mutation event - first")
514
- this.enqueueEvent(evnt)
515
- this.handleQueue()
516
- }
517
- else {
518
- log("mutation event - queueing")
519
- this.enqueueEvent(evnt)
520
- }
521
- },
522
- enqueueEvent: function(evnt) {
523
- var eventCovered = false
524
- var uncovered = []
525
- forEach(this.eventQueue, function(val) {
526
- eventCovered = eventCovered || jQuery.contains(val.target, evnt.target)
527
- if (!(jQuery.contains(evnt.target, val.target))) {
528
- uncovered.push(val)
529
- }
530
- })
531
- if(!eventCovered) {
532
- uncovered.unshift(evnt)
533
- this.eventQueue = uncovered
534
- }
535
- },
536
- handleQueue: function(){
537
- while (this.eventQueue.length != 0){
538
- this.applyAll(this.eventQueue[0].target)
539
- this.eventQueue.shift()
540
- }
541
- },
542
- applyBehaviorsTo: function(element, behaviors) {
543
- return this.applyBehaviorsInContext(new RootContext, element, behaviors)
544
- },
545
- applyBehaviorsInContext: function(context, element, behaviors) {
546
- var curContext,
547
- applyList = [],
548
- scribe = new EventScribe
549
- Ninja.tools.enrich(scribe.handlers, context.eventHandlerSet)
550
-
551
- behaviors = behaviors.sort(function(left, right) {
552
- if(left.priority != right.priority) {
553
- if(left.priority === undefined) {
554
- return -1
555
- }
556
- else if(right.priority === undefined) {
557
- return 1
558
- }
559
- else {
560
- return left.priority - right.priority
561
- }
562
- }
563
- else {
564
- return left.lexicalOrder - right.lexicalOrder
565
- }
566
- }
567
- )
568
-
569
- forEach(behaviors,
570
- function(behavior){
571
- //XXX This needs to have exception handling back
572
- try {
573
- curContext = behavior.inContext(context)
574
- element = behavior.applyTransform(curContext, element)
575
-
576
- context = curContext
577
- context.element = element
578
-
579
- scribe.recordEventHandlers(context, behavior)
580
- }
581
- catch(ex) {
582
- if(ex instanceof TransformFailedException) {
583
- log("!!! Transform failed")
584
- }
585
- else {
586
- log(ex)
587
- throw ex
588
- }
589
- }
590
- }
591
- )
592
- jQuery(element).data("ninja-visited", context)
593
-
594
- scribe.applyEventHandlers(element)
595
- Ninja.tools.enrich(context.eventHandlerSet, scribe.handlers)
596
-
597
- this.fireMutationEvent()
598
-
599
- return element
600
- },
601
- collectBehaviors: function(element, collection, behaviors) {
602
- forEach(behaviors, function(val) {
603
- try {
604
- collection.push(val.choose(element))
605
- }
606
- catch(ex) {
607
- if(ex instanceof CouldntChooseException) {
608
- log("!!! couldn't choose")
609
- }
610
- else {
611
- log(ex)
612
- throw(ex)
613
- }
614
- }
615
- })
616
- },
617
- //XXX Still doesn't quite handle the sub-behavior case - order of application
618
- apply: function(element, startBehaviors, selectorIndex) {
619
- var applicableBehaviors = [], len = this.selectors.length
620
- this.collectBehaviors(element, applicableBehaviors, startBehaviors)
621
- var context = jQuery(element).data('ninja-visited')
622
- if (!context) {
623
- if(typeof selectorIndex == "undefined") {
624
- selectorIndex = 0
625
- }
626
- for(var j = selectorIndex; j < len; j++) {
627
- if(jQuery(element).is(this.selectors[j])) {
628
- this.collectBehaviors(element, applicableBehaviors, this.behaviors[this.selectors[j]])
629
- }
630
- }
631
- this.applyBehaviorsTo(element, applicableBehaviors)
632
- }
633
- else {
634
- context.unbindHandlers()
635
- this.applyBehaviorsInContext(context, element, applicableBehaviors)
636
- }
637
- },
638
- applyAll: function(root){
639
- var len = this.selectors.length
640
- for(var i = 0; i < len; i++) {
641
- var collection = this
642
-
643
- //Sizzle?
644
- jQuery(root).find(this.selectors[i]).each(
645
- function(index, elem){
646
- if (!jQuery(elem).data("ninja-visited")) { //Pure optimization
647
- collection.apply(elem, [], i)
648
- }
649
- }
650
- )
651
- }
652
- }
653
- }
654
-
655
- function Metabehavior(setup, callback) {
656
- setup(this)
657
- this.chooser = callback
658
- }
659
-
660
- Metabehavior.prototype = {
661
- choose: function(element) {
662
- var chosen = this.chooser(element)
663
- if(chosen !== undefined) {
664
- return chosen.choose(element)
665
- }
666
- else {
667
- throw new CouldntChooseException
668
- }
669
- }
670
- }
671
-
672
- //For these to be acceptable, I need to fit them into the pattern that
673
- //Ninja.behavior accepts...
674
- function Selectabehavior(menu) {
675
- this.menu = menu
676
- }
677
-
678
- Selectabehavior.prototype = {
679
- choose: function(element) {
680
- for(var selector in this.menu) {
681
- if(jQuery(element).is(selector)) {
682
- return this.menu[selector].choose(element)
683
- }
684
- }
685
- return null //XXX Should raise exception
686
- }
687
- }
688
-
689
- function Behavior(handlers) {
690
- this.helpers = {}
691
- this.eventHandlers = []
692
- this.lexicalOrder = 0
693
- this.priority = 0
694
-
695
- if (typeof handlers.transform == "function") {
696
- this.transform = handlers.transform
697
- delete handlers.transform
698
- }
699
- if (typeof handlers.helpers != "undefined"){
700
- this.helpers = handlers.helpers
701
- delete handlers.helpers
702
- }
703
- if (typeof handlers.priority != "undefined"){
704
- this.priority = handlers.priority
705
- }
706
- delete handlers.priority
707
- if (typeof handlers.events != "undefined") {
708
- this.eventHandlers = handlers.events
709
- }
710
- else {
711
- this.eventHandlers = handlers
712
- }
713
-
714
- return this
715
- }
716
- Behavior.prototype = {
717
- //XXX applyTo?
718
- apply: function(elem) {
719
- var context = this.inContext({})
720
-
721
- elem = this.applyTransform(context, elem)
722
- jQuery(elem).data("ninja-visited", context)
723
-
724
- this.applyEventHandlers(context, elem)
725
-
726
- return elem
727
- },
728
- priority: function(value) {
729
- this.priority = value
730
- return this
731
- },
732
- choose: function(element) {
733
- return this
734
- },
735
- inContext: function(basedOn) {
736
- function Context() {}
737
- Context.prototype = basedOn
738
- return Ninja.tools.enrich(new Context, this.helpers)
739
- },
740
- applyTransform: function(context, elem) {
741
- var previousElem = elem
742
- var newElem = this.transform.call(context, elem)
743
- if(newElem === undefined) {
744
- return previousElem
745
- }
746
- else {
747
- return newElem
748
- }
749
- },
750
- applyEventHandlers: function(context, elem) {
751
- for(var eventName in this.eventHandlers) {
752
- var handler = this.eventHandlers[eventName]
753
- jQuery(elem).bind(eventName, this.makeHandler.call(context, handler))
754
- }
755
- return elem
756
- },
757
- recordEventHandlers: function(scribe, context) {
758
- for(var eventName in this.eventHandlers) {
759
- scribe.recordHandler(this, eventName, function(oldHandler){
760
- return this.makeHandler.call(context, this.eventHandlers[eventName], oldHandler)
761
- }
762
- )
763
- }
764
- },
765
- buildHandler: function(context, eventName, previousHandler) {
766
- var handle
767
- var stopDefault = true
768
- var stopPropagate = true
769
- var stopImmediate = false
770
- var fireMutation = false
771
- var config = this.eventHandlers[eventName]
772
-
773
- if (typeof config == "function") {
774
- handle = config
775
- }
776
- else {
777
- handle = config[0]
778
- config = config.slice(1,config.length)
779
- var len = config.length
780
- for(var i = 0; i < len; i++) {
781
- if (config[i] == "andDoDefault" || config[i] == "allowDefault") {
782
- stopDefault = false
783
- }
784
- if (config[i] == "allowPropagate" || config[i] == "dontStopPropagation") {
785
- stopPropagate = false
786
- }
787
- //stopImmediatePropagation is a jQuery thing
788
- if (config[i] == "andDoOthers") {
789
- stopImmediate = false
790
- }
791
- if (config[i] == "changesDOM") {
792
- fireMutation = true
793
- }
794
- }
795
- }
796
- var handler = function(eventRecord) {
797
- handle.call(context, eventRecord, this, previousHandler)
798
- return !stopDefault
799
- }
800
- if(stopDefault) {
801
- handler = this.prependAction(handler, function(eventRecord) {
802
- eventRecord.preventDefault()
803
- })
804
- }
805
- if(stopPropagate) {
806
- handler = this.prependAction(handler, function(eventRecord) {
807
- eventRecord.stopPropagation()
808
- })
809
- }
810
- if (stopImmediate) {
811
- handler = this.prependAction(handler, function(eventRecord) {
812
- eventRecord.stopImmediatePropagation()
813
- })
814
- }
815
- if (fireMutation) {
816
- handler = this.appendAction(handler, function(eventRecord) {
817
- Ninja.tools.fireMutationEvent()
818
- })
819
- }
820
-
821
- return handler
822
- },
823
- prependAction: function(handler, doWhat) {
824
- return function(eventRecord) {
825
- doWhat.call(this, eventRecord)
826
- handler.call(this, eventRecord)
827
- }
828
- },
829
- appendAction: function(handler, doWhat) {
830
- return function(eventRecord) {
831
- handler.call(this, eventRecord)
832
- doWhat.call(this, eventRecord)
833
- }
834
- },
835
- transform: function(elem){
836
- return elem
837
- }
838
- }
839
-
840
- return Ninja;
841
- })();
842
-
843
- (function() {
844
- function standardBehaviors(ninja){
845
- return {
846
- // START READING HERE
847
- //Stock behaviors
848
-
849
- //Converts either a link or a form to send its requests via AJAX - we eval
850
- //the Javascript we get back. We get an busy overlay if configured to do
851
- //so.
852
- //
853
- //This farms out the actual behavior to submitsAsAjaxLink and
854
- //submitsAsAjaxForm, c.f.
855
- submitsAsAjax: function(configs) {
856
- return new ninja.chooses(function(meta) {
857
- meta.asLink = Ninja.submitsAsAjaxLink(configs),
858
- meta.asForm = Ninja.submitsAsAjaxForm(configs)
859
- },
860
- function(elem) {
861
- switch(elem.tagName.toLowerCase()) {
862
- case "a": return this.asLink
863
- case "form": return this.asForm
864
- }
865
- })
866
- },
867
-
868
-
869
- //Converts a link to send its GET request via Ajax - we assume that we get
870
- //Javascript back, which is eval'd. While we're waiting, we'll throw up a
871
- //busy overlay if configured to do so. By default, we don't use a busy
872
- //overlay.
873
- //
874
- //Ninja.submitAsAjaxLink({
875
- // busyElement: function(elem) { elem.parent }
876
- //})
877
- //
878
- submitsAsAjaxLink: function(configs) {
879
- configs = Ninja.tools.ensureDefaults(configs,
880
- { busyElement: function(elem) {
881
- return $(elem).parents('address,blockquote,body,dd,div,p,dl,dt,table,form,ol,ul,tr')[0]
882
- }})
883
-
884
- return new ninja.does({
885
- priority: 10,
886
- helpers: {
887
- findOverlay: function(elem) {
888
- return this.deriveElementsFrom(elem, configs.busyElement)
889
- }
890
- },
891
- events: {
892
- click: function(evnt) {
893
- var overlay = this.busyOverlay(this.findOverlay(evnt.target))
894
- var submitter = this.ajaxSubmitter()
895
- submitter.action = evnt.target.href
896
- submitter.method = this.extractMethod(evnt.target)
897
-
898
- submitter.onResponse = function(xhr, statusTxt) {
899
- overlay.remove()
900
- }
901
- overlay.affix()
902
- submitter.submit()
903
- }
904
- }
905
- })
906
- },
907
-
908
- //Converts a form to send its request via Ajax - we assume that we get
909
- //Javascript back, which is eval'd. We pull the method from the form:
910
- //either from the method attribute itself, a data-method attribute or a
911
- //Method input. While we're waiting, we'll throw up a busy overlay if
912
- //configured to do so. By default, we use the form itself as the busy
913
- //element.
914
- //
915
- //Ninja.submitAsAjaxForm({
916
- // busyElement: function(elem) { elem.parent }
917
- //})
918
- //
919
- submitsAsAjaxForm: function(configs) {
920
- configs = Ninja.tools.ensureDefaults(configs,
921
- { busyElement: undefined })
922
-
923
- return new ninja.does({
924
- priority: 20,
925
- helpers: {
926
- findOverlay: function(elem) {
927
- return this.deriveElementsFrom(elem, configs.busyElement)
928
- }
929
- },
930
- events: {
931
- submit: function(evnt) {
932
- var overlay = this.busyOverlay(this.findOverlay(evnt.target))
933
- var submitter = this.ajaxSubmitter()
934
- submitter.formData = jQuery(evnt.target).serializeArray()
935
- submitter.action = evnt.target.action
936
- submitter.method = this.extractMethod(evnt.target, submitter.formData)
937
-
938
- submitter.onResponse = function(xhr, statusTxt) {
939
- overlay.remove()
940
- }
941
- overlay.affix()
942
- submitter.submit()
943
- }
944
- }
945
- })
946
- },
947
-
948
-
949
- //Converts a whole form into a link that submits via AJAX. The intention
950
- //is that you create a <form> elements with hidden inputs and a single
951
- //submit button - then when we transform it, you don't lose anything in
952
- //terms of user interface. Like submitsAsAjaxForm, it will put up a
953
- //busy overlay - by default we overlay the element itself
954
- //
955
- //this.becomesAjaxLink({
956
- // busyElement: function(elem) { jQuery("#user-notification") }
957
- //})
958
- becomesAjaxLink: function(configs) {
959
- configs = Ninja.tools.ensureDefaults(configs, {
960
- busyElement: undefined,
961
- retainAttributes: ["id", "class", "lang", "dir", "title", "data-.*"]
962
- })
963
-
964
- return [ Ninja.submitsAsAjax(configs), Ninja.becomesLink(configs) ]
965
- },
966
-
967
- //Replaces a form with a link - the text of the link is based on the Submit
968
- //input of the form. The form itself is pulled out of the document until
969
- //the link is clicked, at which point, it gets stuffed back into the
970
- //document and submitted, so the link behaves exactly link submitting the
971
- //form with its default inputs. The motivation is to use hidden-input-only
972
- //forms for POST interactions, which Javascript can convert into links if
973
- //you want.
974
- becomesLink: function(configs) {
975
- configs = Ninja.tools.ensureDefaults(configs, {
976
- retainAttributes: ["id", "class", "lang", "dir", "title", "rel", "data-.*"]
977
- })
978
-
979
- return new ninja.does({
980
- priority: 30,
981
- transform: function(form){
982
- var linkText
983
- if ((images = jQuery('input[type=image]', form)).size() > 0){
984
- image = images[0]
985
- linkText = "<img src='" + image.src + "' alt='" + image.alt +"'";
986
- }
987
- else if((submits = jQuery('input[type=submit]', form)).size() > 0) {
988
- submit = submits[0]
989
- if(submits.size() > 1) {
990
- log("Multiple submits. Using: " + submit)
991
- }
992
- linkText = submit.value
993
- }
994
- else {
995
- log("Couldn't find a submit input in form");
996
- this.cantTransform()
997
- }
998
-
999
- var link = jQuery("<a rel='nofollow' href='#'>" + linkText + "</a>")
1000
- this.copyAttributes(form, link, configs.retainAttributes)
1001
- this.stash(jQuery(form).replaceWith(link))
1002
- return link
1003
- },
1004
- events: {
1005
- click: function(evnt, elem){
1006
- this.cascadeEvent("submit")
1007
- }
1008
- }
1009
- })
1010
-
1011
- },
1012
-
1013
- //Use for elements that should be transient. For instance, the default
1014
- //behavior of failed AJAX calls is to insert a message into a
1015
- //div#messages with a "flash" class. You can use this behavior to have
1016
- //those disappear after a few seconds.
1017
- //
1018
- //Configs:
1019
- //{ lifetime: 10000, diesFor: 600 }
1020
-
1021
- decays: function(configs) {
1022
- configs = Ninja.tools.ensureDefaults(configs, {
1023
- lifetime: 10000,
1024
- diesFor: 600
1025
- })
1026
-
1027
- return new ninja.does({
1028
- priority: 100,
1029
- transform: function(elem) {
1030
- jQuery(elem).delay(configs.lifetime).slideUp(configs.diesFor, function(){
1031
- jQuery(elem).remove()
1032
- Ninja.tools.fireMutationEvent()
1033
- })
1034
- },
1035
- events: {
1036
- click: [function(event) {
1037
- jQuery(this.element).remove();
1038
- }, "changesDOM"]
1039
- }
1040
- })
1041
- }
1042
- };
1043
- }
1044
-
1045
- Ninja.packageBehaviors(standardBehaviors)
1046
- })();
1047
-
1048
- (function($){
1049
- function uiBehaviors(ninja){
1050
- function watermarkedSubmitter(inputBehavior) {
1051
- return new ninja.does({
1052
- priority: 1000,
1053
- submit: [function(event, el, oldHandler) {
1054
- inputBehavior.prepareForSubmit()
1055
- oldHandler(event)
1056
- }, "andDoDefault"]
1057
- })
1058
- }
1059
- function isWatermarkedPassword(configs) {
1060
- return new ninja.does({
1061
- priority: 1000,
1062
- helpers: {
1063
- prepareForSubmit: function() {
1064
- if($(this.element).hasClass('ninja_watermarked')) {
1065
- $(this.element).val('')
1066
- }
1067
- },
1068
- },
1069
- transform: function(element) {
1070
- var label = $('label[for=' + $(element)[0].id + ']')
1071
- if(label.length == 0) {
1072
- this.cantTransform()
1073
- }
1074
- label.addClass('ninja_watermarked')
1075
- this.watermarkText = label.text()
1076
-
1077
- var el = $(element)
1078
- el.addClass('ninja_watermarked')
1079
- el.val(this.watermarkText)
1080
- el.attr("type", "text")
1081
-
1082
- this.applyBehaviors(el.parents('form')[0], [watermarkedSubmitter(this)])
1083
-
1084
- return element
1085
- },
1086
- events: {
1087
- focus: function(event) {
1088
- $(this.element).removeClass('ninja_watermarked').val('').attr("type", "password")
1089
- },
1090
- blur: function(event) {
1091
- if($(this.element).val() == '') {
1092
- $(this.element).addClass('ninja_watermarked').val(this.watermarkText).attr("type", "text")
1093
- }
1094
- }
1095
- }
1096
- })
1097
- }
1098
-
1099
- function isWatermarkedText(configs) {
1100
- return new ninja.does({
1101
- priority: 1000,
1102
- helpers: {
1103
- prepareForSubmit: function() {
1104
- if($(this.element).hasClass('ninja_watermarked')) {
1105
- $(this.element).val('')
1106
- }
1107
- },
1108
- },
1109
- transform: function(element) {
1110
- var label = $('label[for=' + $(element)[0].id + ']')
1111
- if(label.length == 0) {
1112
- this.cantTransform()
1113
- }
1114
- label.addClass('ninja_watermarked')
1115
- this.watermarkText = label.text()
1116
- var el = $(element)
1117
- el.addClass('ninja_watermarked')
1118
- el.val(this.watermarkText)
1119
-
1120
- this.applyBehaviors(el.parents('form')[0], [watermarkedSubmitter(this)])
1121
-
1122
- return element
1123
- },
1124
- events: {
1125
- focus: function(event) {
1126
- if($(this.element).hasClass('ninja_watermarked')) {
1127
- $(this.element).removeClass('ninja_watermarked').val('')
1128
- }
1129
- },
1130
- blur: function(event) {
1131
- if($(this.element).val() == '') {
1132
- $(this.element).addClass('ninja_watermarked').val(this.watermarkText)
1133
- }
1134
- }
1135
- }
1136
- })
1137
- }
1138
-
1139
- return {
1140
- isWatermarked: function(configs) {
1141
- return new ninja.chooses(function(meta) {
1142
- meta.asText = isWatermarkedText(configs)
1143
- meta.asPassword = isWatermarkedPassword(configs)
1144
- },
1145
- function(elem) {
1146
- if($(elem).is("input[type=text],textarea")) {
1147
- return this.asText
1148
- }
1149
- //Seems IE has a thing about changing input types...
1150
- //We'll get back to this one
1151
- // else if($(elem).is("input[type=password]")){
1152
- // return this.asPassword
1153
- // }
1154
- })
1155
- }
1156
- }
1157
- }
1158
-
1159
- Ninja.packageBehaviors(uiBehaviors)
1160
- })(jQuery);
1161
-
1162
-
1163
- //This exists to carry over interfaces from earlier versions of Ninjascript. Likely, it will be removed from future versions of NinjaScript
1164
- ( function($) {
1165
- $.extend(
1166
- {
1167
- ninja: Ninja,
1168
- behavior: Ninja.behavior
1169
- }
1170
- );
1171
- }
1172
- )(jQuery);