mizugumo 0.1.4 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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);