1 |
|
2 | var opts = window.syn ? window.syn : {};
|
3 | var extend = function (d, s) {
|
4 | var p;
|
5 | for (p in s) {
|
6 | d[p] = s[p];
|
7 | }
|
8 | return d;
|
9 | }, browser = {
|
10 | msie: !!(window.attachEvent && !window.opera) || navigator.userAgent.indexOf('Trident/') > -1,
|
11 | opera: !!window.opera,
|
12 | webkit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
|
13 | safari: navigator.userAgent.indexOf('AppleWebKit/') > -1 && navigator.userAgent.indexOf('Chrome/') === -1,
|
14 | gecko: navigator.userAgent.indexOf('Gecko') > -1,
|
15 | mobilesafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/),
|
16 | rhino: navigator.userAgent.match(/Rhino/) && true,
|
17 | chrome: !!window.chrome && !!window.chrome.webstore
|
18 | }, createEventObject = function (type, options, element) {
|
19 | var event = element.ownerDocument.createEventObject();
|
20 | return extend(event, options);
|
21 | }, data = {}, id = 1, expando = '_synthetic' + new Date().getTime(), bind, unbind, schedule, key = /keypress|keyup|keydown/, page = /load|unload|abort|error|select|change|submit|reset|focus|blur|resize|scroll/, activeElement, syn = function (type, element, options, callback) {
|
22 | return new syn.init(type, element, options, callback);
|
23 | };
|
24 | syn.config = opts;
|
25 | syn.__tryFocus = function tryFocus(element) {
|
26 | try {
|
27 | element.focus();
|
28 | } catch (e) {
|
29 | }
|
30 | };
|
31 | bind = function (el, ev, f) {
|
32 | return el.addEventListener ? el.addEventListener(ev, f, false) : el.attachEvent('on' + ev, f);
|
33 | };
|
34 | unbind = function (el, ev, f) {
|
35 | return el.addEventListener ? el.removeEventListener(ev, f, false) : el.detachEvent('on' + ev, f);
|
36 | };
|
37 | schedule = syn.config.schedule || function (fn, ms) {
|
38 | setTimeout(fn, ms);
|
39 | };
|
40 | extend(syn, {
|
41 | init: function (type, element, options, callback) {
|
42 | var args = syn.args(options, element, callback), self = this;
|
43 | this.queue = [];
|
44 | this.element = args.element;
|
45 | if (typeof this[type] === 'function') {
|
46 | this[type](args.element, args.options, function (defaults, el) {
|
47 | if (args.callback) {
|
48 | args.callback.apply(self, arguments);
|
49 | }
|
50 | self.done.apply(self, arguments);
|
51 | });
|
52 | } else {
|
53 | this.result = syn.trigger(args.element, type, args.options);
|
54 | if (args.callback) {
|
55 | args.callback.call(this, args.element, this.result);
|
56 | }
|
57 | }
|
58 | },
|
59 | jquery: function (el, fast) {
|
60 | if (window.FuncUnit && window.FuncUnit.jQuery) {
|
61 | return window.FuncUnit.jQuery;
|
62 | }
|
63 | if (el) {
|
64 | return syn.helpers.getWindow(el).jQuery || window.jQuery;
|
65 | } else {
|
66 | return window.jQuery;
|
67 | }
|
68 | },
|
69 | args: function () {
|
70 | var res = {}, i = 0;
|
71 | for (; i < arguments.length; i++) {
|
72 | if (typeof arguments[i] === 'function') {
|
73 | res.callback = arguments[i];
|
74 | } else if (arguments[i] && arguments[i].jquery) {
|
75 | res.element = arguments[i][0];
|
76 | } else if (arguments[i] && arguments[i].nodeName) {
|
77 | res.element = arguments[i];
|
78 | } else if (res.options && typeof arguments[i] === 'string') {
|
79 | res.element = document.getElementById(arguments[i]);
|
80 | } else if (arguments[i]) {
|
81 | res.options = arguments[i];
|
82 | }
|
83 | }
|
84 | return res;
|
85 | },
|
86 | click: function (element, options, callback) {
|
87 | syn('click!', element, options, callback);
|
88 | },
|
89 | defaults: {
|
90 | focus: function focus() {
|
91 | if (!syn.support.focusChanges) {
|
92 | var element = this, nodeName = element.nodeName.toLowerCase();
|
93 | syn.data(element, 'syntheticvalue', element.value);
|
94 | if (nodeName === 'input' || nodeName === 'textarea') {
|
95 | bind(element, 'blur', function blur() {
|
96 | if (syn.data(element, 'syntheticvalue') !== element.value) {
|
97 | syn.trigger(element, 'change', {});
|
98 | }
|
99 | unbind(element, 'blur', blur);
|
100 | });
|
101 | }
|
102 | }
|
103 | },
|
104 | submit: function () {
|
105 | syn.onParents(this, function (el) {
|
106 | if (el.nodeName.toLowerCase() === 'form') {
|
107 | el.submit();
|
108 | return false;
|
109 | }
|
110 | });
|
111 | }
|
112 | },
|
113 | changeOnBlur: function (element, prop, value) {
|
114 | bind(element, 'blur', function onblur() {
|
115 | if (value !== element[prop]) {
|
116 | syn.trigger(element, 'change', {});
|
117 | }
|
118 | unbind(element, 'blur', onblur);
|
119 | });
|
120 | },
|
121 | closest: function (el, type) {
|
122 | while (el && el.nodeName.toLowerCase() !== type.toLowerCase()) {
|
123 | el = el.parentNode;
|
124 | }
|
125 | return el;
|
126 | },
|
127 | data: function (el, key, value) {
|
128 | var d;
|
129 | if (!el[expando]) {
|
130 | el[expando] = id++;
|
131 | }
|
132 | if (!data[el[expando]]) {
|
133 | data[el[expando]] = {};
|
134 | }
|
135 | d = data[el[expando]];
|
136 | if (value) {
|
137 | data[el[expando]][key] = value;
|
138 | } else {
|
139 | return data[el[expando]][key];
|
140 | }
|
141 | },
|
142 | onParents: function (el, func) {
|
143 | var res;
|
144 | while (el && res !== false) {
|
145 | res = func(el);
|
146 | el = el.parentNode;
|
147 | }
|
148 | return el;
|
149 | },
|
150 | focusable: /^(a|area|frame|iframe|label|input|select|textarea|button|html|object)$/i,
|
151 | isFocusable: function (elem) {
|
152 | var attributeNode;
|
153 | if (elem.getAttributeNode) {
|
154 | attributeNode = elem.getAttributeNode('tabIndex');
|
155 | }
|
156 | return this.focusable.test(elem.nodeName) || attributeNode && attributeNode.specified && syn.isVisible(elem);
|
157 | },
|
158 | isVisible: function (elem) {
|
159 | return elem.offsetWidth && elem.offsetHeight || elem.clientWidth && elem.clientHeight;
|
160 | },
|
161 | tabIndex: function (elem) {
|
162 | var attributeNode = elem.getAttributeNode('tabIndex');
|
163 | return attributeNode && attributeNode.specified && (parseInt(elem.getAttribute('tabIndex')) || 0);
|
164 | },
|
165 | bind: bind,
|
166 | unbind: unbind,
|
167 | schedule: schedule,
|
168 | browser: browser,
|
169 | helpers: {
|
170 | createEventObject: createEventObject,
|
171 | createBasicStandardEvent: function (type, defaults, doc) {
|
172 | var event;
|
173 | try {
|
174 | event = doc.createEvent('Events');
|
175 | } catch (e2) {
|
176 | event = doc.createEvent('UIEvents');
|
177 | } finally {
|
178 | event.initEvent(type, true, true);
|
179 | extend(event, defaults);
|
180 | }
|
181 | return event;
|
182 | },
|
183 | inArray: function (item, array) {
|
184 | var i = 0;
|
185 | for (; i < array.length; i++) {
|
186 | if (array[i] === item) {
|
187 | return i;
|
188 | }
|
189 | }
|
190 | return -1;
|
191 | },
|
192 | getWindow: function (element) {
|
193 | if (element.ownerDocument) {
|
194 | return element.ownerDocument.defaultView || element.ownerDocument.parentWindow;
|
195 | }
|
196 | },
|
197 | extend: extend,
|
198 | scrollOffset: function (win, set) {
|
199 | var doc = win.document.documentElement, body = win.document.body;
|
200 | if (set) {
|
201 | window.scrollTo(set.left, set.top);
|
202 | } else {
|
203 | return {
|
204 | left: (doc && doc.scrollLeft || body && body.scrollLeft || 0) + (doc.clientLeft || 0),
|
205 | top: (doc && doc.scrollTop || body && body.scrollTop || 0) + (doc.clientTop || 0)
|
206 | };
|
207 | }
|
208 | },
|
209 | scrollDimensions: function (win) {
|
210 | var doc = win.document.documentElement, body = win.document.body, docWidth = doc.clientWidth, docHeight = doc.clientHeight, compat = win.document.compatMode === 'CSS1Compat';
|
211 | return {
|
212 | height: compat && docHeight || body.clientHeight || docHeight,
|
213 | width: compat && docWidth || body.clientWidth || docWidth
|
214 | };
|
215 | },
|
216 | addOffset: function (options, el) {
|
217 | var jq = syn.jquery(el), off;
|
218 | if (typeof options === 'object' && options.clientX === undefined && options.clientY === undefined && options.pageX === undefined && options.pageY === undefined && jq) {
|
219 | el = jq(el);
|
220 | off = el.offset();
|
221 | options.pageX = off.left + el.width() / 2;
|
222 | options.pageY = off.top + el.height() / 2;
|
223 | }
|
224 | }
|
225 | },
|
226 | key: {
|
227 | ctrlKey: null,
|
228 | altKey: null,
|
229 | shiftKey: null,
|
230 | metaKey: null
|
231 | },
|
232 | dispatch: function (event, element, type, autoPrevent) {
|
233 | if (element.dispatchEvent && event) {
|
234 | var preventDefault = event.preventDefault, prevents = autoPrevent ? -1 : 0;
|
235 | if (autoPrevent) {
|
236 | bind(element, type, function ontype(ev) {
|
237 | ev.preventDefault();
|
238 | unbind(this, type, ontype);
|
239 | });
|
240 | }
|
241 | event.preventDefault = function () {
|
242 | prevents++;
|
243 | if (++prevents > 0) {
|
244 | preventDefault.apply(this, []);
|
245 | }
|
246 | };
|
247 | element.dispatchEvent(event);
|
248 | return prevents <= 0;
|
249 | } else {
|
250 | try {
|
251 | window.event = event;
|
252 | } catch (e) {
|
253 | }
|
254 | return element.sourceIndex <= 0 || element.fireEvent && element.fireEvent('on' + type, event);
|
255 | }
|
256 | },
|
257 | create: {
|
258 | page: {
|
259 | event: function (type, options, element) {
|
260 | var doc = syn.helpers.getWindow(element).document || document, event;
|
261 | if (doc.createEvent) {
|
262 | event = doc.createEvent('Events');
|
263 | event.initEvent(type, true, true);
|
264 | return event;
|
265 | } else {
|
266 | try {
|
267 | event = createEventObject(type, options, element);
|
268 | } catch (e) {
|
269 | }
|
270 | return event;
|
271 | }
|
272 | }
|
273 | },
|
274 | focus: {
|
275 | event: function (type, options, element) {
|
276 | syn.onParents(element, function (el) {
|
277 | if (syn.isFocusable(el)) {
|
278 | if (el.nodeName.toLowerCase() !== 'html') {
|
279 | syn.__tryFocus(el);
|
280 | activeElement = el;
|
281 | } else if (activeElement) {
|
282 | var doc = syn.helpers.getWindow(element).document;
|
283 | if (doc !== window.document) {
|
284 | return false;
|
285 | } else if (doc.activeElement) {
|
286 | doc.activeElement.blur();
|
287 | activeElement = null;
|
288 | } else {
|
289 | activeElement.blur();
|
290 | activeElement = null;
|
291 | }
|
292 | }
|
293 | return false;
|
294 | }
|
295 | });
|
296 | return true;
|
297 | }
|
298 | }
|
299 | },
|
300 | support: {
|
301 | clickChanges: false,
|
302 | clickSubmits: false,
|
303 | keypressSubmits: false,
|
304 | mouseupSubmits: false,
|
305 | radioClickChanges: false,
|
306 | focusChanges: false,
|
307 | linkHrefJS: false,
|
308 | keyCharacters: false,
|
309 | backspaceWorks: false,
|
310 | mouseDownUpClicks: false,
|
311 | tabKeyTabs: false,
|
312 | keypressOnAnchorClicks: false,
|
313 | optionClickBubbles: false,
|
314 | pointerEvents: false,
|
315 | touchEvents: false,
|
316 | ready: 0
|
317 | },
|
318 | trigger: function (element, type, options) {
|
319 | if (!options) {
|
320 | options = {};
|
321 | }
|
322 | var create = syn.create, setup = create[type] && create[type].setup, kind = key.test(type) ? 'key' : page.test(type) ? 'page' : 'mouse', createType = create[type] || {}, createKind = create[kind], event, ret, autoPrevent, dispatchEl = element;
|
323 | if (syn.support.ready === 2 && setup) {
|
324 | setup(type, options, element);
|
325 | }
|
326 | autoPrevent = options._autoPrevent;
|
327 | delete options._autoPrevent;
|
328 | if (createType.event) {
|
329 | ret = createType.event(type, options, element);
|
330 | } else {
|
331 | options = createKind.options ? createKind.options(type, options, element) : options;
|
332 | if (!syn.support.changeBubbles && /option/i.test(element.nodeName)) {
|
333 | dispatchEl = element.parentNode;
|
334 | }
|
335 | event = createKind.event(type, options, dispatchEl);
|
336 | ret = syn.dispatch(event, dispatchEl, type, autoPrevent);
|
337 | }
|
338 | if (ret && syn.support.ready === 2 && syn.defaults[type]) {
|
339 | syn.defaults[type].call(element, options, autoPrevent);
|
340 | }
|
341 | return ret;
|
342 | },
|
343 | eventSupported: function (eventName) {
|
344 | var el = document.createElement('div');
|
345 | eventName = 'on' + eventName;
|
346 | var isSupported = eventName in el;
|
347 | if (!isSupported) {
|
348 | el.setAttribute(eventName, 'return;');
|
349 | isSupported = typeof el[eventName] === 'function';
|
350 | }
|
351 | el = null;
|
352 | return isSupported;
|
353 | }
|
354 | });
|
355 | extend(syn.init.prototype, {
|
356 | then: function (type, element, options, callback) {
|
357 | if (syn.autoDelay) {
|
358 | this.delay();
|
359 | }
|
360 | var args = syn.args(options, element, callback), self = this;
|
361 | this.queue.unshift(function (el, prevented) {
|
362 | if (typeof this[type] === 'function') {
|
363 | this.element = args.element || el;
|
364 | this[type](this.element, args.options, function (defaults, el) {
|
365 | if (args.callback) {
|
366 | args.callback.apply(self, arguments);
|
367 | }
|
368 | self.done.apply(self, arguments);
|
369 | });
|
370 | } else {
|
371 | this.result = syn.trigger(args.element, type, args.options);
|
372 | if (args.callback) {
|
373 | args.callback.call(this, args.element, this.result);
|
374 | }
|
375 | return this;
|
376 | }
|
377 | });
|
378 | return this;
|
379 | },
|
380 | delay: function (timeout, callback) {
|
381 | if (typeof timeout === 'function') {
|
382 | callback = timeout;
|
383 | timeout = null;
|
384 | }
|
385 | timeout = timeout || 600;
|
386 | var self = this;
|
387 | this.queue.unshift(function () {
|
388 | schedule(function () {
|
389 | if (callback) {
|
390 | callback.apply(self, []);
|
391 | }
|
392 | self.done.apply(self, arguments);
|
393 | }, timeout);
|
394 | });
|
395 | return this;
|
396 | },
|
397 | done: function (defaults, el) {
|
398 | if (el) {
|
399 | this.element = el;
|
400 | }
|
401 | if (this.queue.length) {
|
402 | this.queue.pop().call(this, this.element, defaults);
|
403 | }
|
404 | },
|
405 | '_click': function (element, options, callback, force) {
|
406 | syn.helpers.addOffset(options, element);
|
407 | if (syn.support.pointerEvents) {
|
408 | syn.trigger(element, 'pointerdown', options);
|
409 | }
|
410 | if (syn.support.touchEvents) {
|
411 | syn.trigger(element, 'touchstart', options);
|
412 | }
|
413 | syn.trigger(element, 'mousedown', options);
|
414 | schedule(function () {
|
415 | if (syn.support.pointerEvents) {
|
416 | syn.trigger(element, 'pointerup', options);
|
417 | }
|
418 | if (syn.support.touchEvents) {
|
419 | syn.trigger(element, 'touchend', options);
|
420 | }
|
421 | syn.trigger(element, 'mouseup', options);
|
422 | if (!syn.support.mouseDownUpClicks || force) {
|
423 | syn.trigger(element, 'click', options);
|
424 | callback(true);
|
425 | } else {
|
426 | syn.create.click.setup('click', options, element);
|
427 | syn.defaults.click.call(element);
|
428 | schedule(function () {
|
429 | callback(true);
|
430 | }, 1);
|
431 | }
|
432 | }, 1);
|
433 | },
|
434 | '_rightClick': function (element, options, callback) {
|
435 | syn.helpers.addOffset(options, element);
|
436 | var mouseopts = extend(extend({}, syn.mouse.browser.right.mouseup), options);
|
437 | if (syn.support.pointerEvents) {
|
438 | syn.trigger(element, 'pointerdown', mouseopts);
|
439 | }
|
440 | syn.trigger(element, 'mousedown', mouseopts);
|
441 | schedule(function () {
|
442 | if (syn.support.pointerEvents) {
|
443 | syn.trigger(element, 'pointerup', mouseopts);
|
444 | }
|
445 | syn.trigger(element, 'mouseup', mouseopts);
|
446 | if (syn.mouse.browser.right.contextmenu) {
|
447 | syn.trigger(element, 'contextmenu', extend(extend({}, syn.mouse.browser.right.contextmenu), options));
|
448 | }
|
449 | callback(true);
|
450 | }, 1);
|
451 | },
|
452 | '_dblclick': function (element, options, callback) {
|
453 | syn.helpers.addOffset(options, element);
|
454 | var self = this;
|
455 | this._click(element, options, function () {
|
456 | schedule(function () {
|
457 | self._click(element, options, function () {
|
458 | syn.trigger(element, 'dblclick', options);
|
459 | callback(true);
|
460 | }, true);
|
461 | }, 2);
|
462 | });
|
463 | }
|
464 | });
|
465 | var actions = [
|
466 | 'click',
|
467 | 'dblclick',
|
468 | 'move',
|
469 | 'drag',
|
470 | 'key',
|
471 | 'type',
|
472 | 'rightClick'
|
473 | ], makeAction = function (name) {
|
474 | syn[name] = function (element, options, callback) {
|
475 | return syn('_' + name, element, options, callback);
|
476 | };
|
477 | syn.init.prototype[name] = function (element, options, callback) {
|
478 | return this.then('_' + name, element, options, callback);
|
479 | };
|
480 | }, i = 0;
|
481 | for (; i < actions.length; i++) {
|
482 | makeAction(actions[i]);
|
483 | }
|
484 | module.exports = syn; |
\ | No newline at end of file |