UNPKG

unserver-unify

Version:

579 lines (574 loc) 18 kB
/*global window, alert, console, HTMLElement, Events */ /*jslint devel: true, browser: true, regexp: true, vars: true, eqeq: true, unparam: true, plusplus: true, nomen: true */ /** * SCOBot Utilities * Module pattern utilized. * This is a series of refined util methods previously used via jQuery. * Due to the abstraction of the framework/library and cross-browser compatibility this is based on and tested * for older browsers. * * SCOBot previously used : * $.extend * $.isFunction * $().triggerHandler * $().on * $.isPlainObject * $.isWindow * $.type * class2type * $.isArray * $().bind * $(obj) * * SCOBot doesn't do any DOM Manipulation, so most of jQuery's appeal is un-needed. * * That said, not to be over simplified - * $(".class") vs document.querySelectorAll(".class"); * $("#id .class"); vs document.querySelectorAll("#id .class"); or document.getElementById("id").querySelectorAll(".class"); * * However, SCOBot did use custom events. Events only fire on DOM objects, so a custom solution was needed. Regardless * of Framework, there are different approaches to this and some are rather extensive. * * If you are using a framework you may have some overlap and may be able to write some hooks into 'like' functionality * without needing this whole file. Option is totally up to you. Cost is 3.9KB minified and packed (not gzip compressed). * * About base64. Newer browsers support atob and btoa, but IE 6, 7, 8 and 9 won't. * var encodedData = window.btoa("Hello, world"); // encode a string * var decodedData = window.atob(encodedData); // decode the string * So this is a tough call here to write more code to support base64. * If you'd like to support it, consider https://github.com/davidchambers/Base64.js/blob/master/base64.js. * It mostly depends if you want to retain the commands above or use JavaScript based approaches. * * https://github.com/cybercussion/SCOBot * * @author Cybercussion Interactive, LLC <info@cybercussion.com> * @license Copyright (c) 2009-2016, Cybercussion Interactive LLC * As of 3.0.0 this code is under a Creative Commons Attribution-ShareAlike 4.0 International License. * @version 4.0.5 * @constructor */ /*! * SCOBot Utility, Updated Jan 3rd, 2016 * Copyright (c) 2009-2016, Cybercussion Interactive LLC. All rights reserved. * As of 3.0.0 this code is under a Creative Commons Attribution-ShareAlike 4.0 International License. */ var SCOBotUtil = function() { // Constructor /////////////// "use strict"; var version = "1.0.4", createDate = "07/23/2013 03:23PM", modifiedDate = "01/03/2015 14:12PM", isReady = false, types = ["Boolean", "Number", "String", "Function", "Array", "Date", "RegExp", "Object"], class_types = [], toString = Object.prototype.toString, hasOwn = Object.prototype.hasOwnProperty, h = 'HTMLEvents', k = 'KeyboardEvent', m = 'MouseEvents', eventTypes = { load: h, unload: h, abort: h, error: h, select: h, change: h, submit: h, reset: h, focus: h, blur: h, resize: h, scroll: h, input: h, keyup: k, keydown: k, click: m, dblclick: m, mousedown: m, mouseup: m, mouseover: m, mousemove: m, mouseout: m, contextmenu: m }, defaults = { clientX: 0, clientY: 0, button: 0, ctrlKey: false, altKey: false, shiftKey: false, metaKey: false, bubbles: true, cancelable: true, view: document.defaultView, key: '', location: 0, modifiers: '', repeat: 0, locale: '' }, initializers = { HTMLEvents: function(el, name, event, o) { return event.initEvent(name, o.bubbles, o.cancelable); }, // Don't need these for SCOBot, but you may need them for your project. /*KeyboardEvent: function (el, name, event, o) { // Use a blank key if not defined and initialize the charCode var key = ('key' in o) ? o.key : "", charCode, modifiers, location = ('location' in o) ? o.location : 0; // 0 is the default location if (event.initKeyboardEvent) { // Chrome and IE9+ uses initKeyboardEvent if (!'modifiers' in o) { modifiers = []; if (o.ctrlKey) modifiers.push("Ctrl"); if (o.altKey) modifiers.push("Alt"); if (o.ctrlKey && o.altKey) modifiers.push("AltGraph"); if (o.shiftKey) modifiers.push("Shift"); if (o.metaKey) modifiers.push("Meta"); modifiers = modifiers.join(" "); } else { modifiers = o.modifiers; } return event.initKeyboardEvent( name, o.bubbles, o.cancelable, o.view, key, location, modifiers, o.repeat, o.locale ); } // Mozilla uses initKeyEvent charCode = (o.hasOwnProperty('charCode')) ? o.charCode : key.charCodeAt(0) || 0; return event.initKeyEvent( name, o.bubbles, o.cancelable, o.view, o.ctrlKey, o.altKey, o.shiftKey, o.metaKey, charCode, key ); }, MouseEvents: function (el, name, event, o) { var screenX = (o.hasOwnProperty('screenX')) ? o.screenX : o.clientX, screenY = (o.hasOwnProperty('screenY')) ? o.screenY : o.clientY, clicks, button; if ('detail' in o) { clicks = o.detail; } else if (name === 'dblclick') { clicks = 2; } else { clicks = 1; } // Default context menu to be a right click if (name === 'contextmenu') { button = button = o.button || 2; } return event.initMouseEvent( name, o.bubbles, o.cancelable, o.view, clicks, screenX, screenY, o.clientX, o.clientY, o.ctrlKey, o.altKey, o.shiftKey, o.metaKey, button, el ); },*/ CustomEvent: function(el, name, event, o) { return event.initCustomEvent(name, o.bubbles, o.cancelable, o.detail); } }, eventSplitter = /\s+/, len = types.length, self = this, checkLoaded = function(h) { if (isReady) { return; } try { // If IE is used, use the trick by Diego Perini // http://javascript.nwbox.com/IEContentLoaded/ document.documentElement.doScroll("left"); } catch (e) { setTimeout(function() { checkLoaded(h); }, 0); return; } // and execute any waiting functions h(); }, /** * type * Private way to check the type null or undefined * @param o * @returns {String} */ type = function(o) { return o == null ? String(o) : class_types[toString.call(o)] || "object"; }, /** * Is Window * @param o * @returns {Boolean} */ isWindow = function(o) { return o != null && o === o.window; // was o.window which I think is wrong. }, isElement = function(o) { return (typeof HTMLElement === "object" ? o instanceof HTMLElement : o && typeof o === "object" && o !== null && o.nodeType === 1 && typeof o.nodeName === "string"); }, ////////////////////////////// // Public //////////////////// /** * Is Plain Object * Will check to see if item passed in is a plain object {} * @param o {Object} * @returns {Boolean} */ isPlainObject = function(o) { if (type(o) !== "object" || o.nodeType || isWindow(o)) { return false; } return !(o.constructor && !hasOwn.call(o.constructor.prototype, "isPrototypeOf")); }, /** * Is Array * Will check to see if item passed in is an array * @param o {Array} * @returns {Boolean} */ isArray = function(o) { return (o instanceof Array) || (toString.apply(o) === '[object Array]'); }, /** * Is Function * @param o * @returns {boolean} */ isFunction = function(o) { return type(o) === "function"; }, /** * Extend * @param o {object} range of objects to merge/extend * @returns {*|{}} */ extend = function(o) { var i = 1, args = arguments, olen = args.length, key; while (i < olen) { for (key in args[i]) { if (args[i].hasOwnProperty(key)) { args[0][key] = args[i][key]; } } i += 1; } return args[0]; }, /** * Add Event * This is tricky because this only works on DOM Objects. * @param target * @param event * @param handler */ addEvent = function(target, event, handler) { if (event.indexOf(' ') >= 0) { // Multi-event - if you are listening to 'setvalue getvalue' var events = event.split(' '), elen = events.length; while (elen--) { // Add events. addEvent(target, events[elen], handler); } } else { if (target === window || isElement(target)) { // DOM Object if (target.addEventListener) { // Standard if (event === "loaded") { event = "DOMContentLoaded"; } target.addEventListener(event, handler, false); } else { // IE 6/7/8 if (event === "loaded") { event = "onreadystatechange"; document.attachEvent(event, function() { if (document.readyState === "complete") { document.detachEvent("onreadystatechange", target); isReady = true; handler(); } }); // If IE and not an iframe if (document.documentElement.doScroll && window === window.top) { checkLoaded(handler); } //} else if (event === "unload") { // We want to ensure we catch IE unload events (still testing) } else { target.attachEvent('on' + event, handler); } } } else { // JavaScript Object extend(target, Events); // add capability target.on(event, handler); // add listener } } }, /** * Calculate Average * @param num_arr * @returns {string} */ calcAverage = function(num_arr) { var sum = 0, i = 0, nlen = num_arr.length; while (i < nlen) { sum += num_arr[i].lat; i += 1; } sum = sum / len; return sum.toFixed(2); }, /** * Trigger Event * @param target * @param name * @param options */ triggerEvent = function(target, name, options) { var doc = document, event, etype, attr; options = options || {}; for (attr in defaults) { if (defaults.hasOwnProperty(attr)) { if (!options.hasOwnProperty(attr)) { options[attr] = defaults[attr]; } } } // Check DOM Element if (isWindow(target) || isElement(target)) { if (doc.createEvent) { // Standard etype = eventTypes[name] || 'CustomEvent'; event = doc.createEvent(etype); initializers[etype](target, name, event, options); try { target.dispatchEvent(event); } catch (ignore) { // doesn't exist } } else { // IE event = doc.createEventObject(); target.fireEvent('on' + etype, event); } } else { // JavaScript Object try { target.trigger(name, options); } catch (ignore) { // nothing listening } } }, // End old ////////////////////////////////////// triggerEvents = function(events, args) { var ev, i = -1, l = events.length, a1 = args[0], a2 = args[1], a3 = args[2], al = args.length; switch (al) { case 0: while (++i < l) { ev = events[i]; ev.callback.call(ev.ctx); } return; case 1: while (++i < l) { ev = events[i]; ev.callback.call(ev.ctx, a1); } return; case 2: while (++i < l) { ev = events[i]; ev.callback.call(ev.ctx, a1, a2); } return; case 3: while (++i < l) { ev = events[i]; ev.callback.call(ev.ctx, a1, a2, a3); } return; default: while (++i < l) { ev = events[i]; ev.callback.apply(ev.ctx, args); } return; } }, eventsApi = function(obj, action, name, rest) { var key, i, l; if (!name) { return true; } // Handle event maps. if (typeof name === 'object') { for (key in name) { if (name.hasOwnProperty(key)) { obj[action].apply(obj, [key, name[key]].concat(rest)); } } return false; } // Handle space separated event names. if (eventSplitter.test(name)) { var names = name.split(eventSplitter); for (i = 0, l = names.length; i < l; i++) { obj[action].apply(obj, [names[i]].concat(rest)); } return false; } return true; }, Events = { on: function(name, callback, context) { if (!eventsApi(this, 'on', name, [callback, context]) || !callback) return this; this._events || (this._events = {}); var events = this._events[name] || (this._events[name] = []); events.push({ callback: callback, context: context, ctx: context || this }); return this; }, once: function(name, callback, context) { if (!eventsApi(this, 'once', name, [callback, context]) || !callback) return this; var self = this; var once = _.once(function() { self.off(name, once); callback.apply(this, arguments); }); once._callback = callback; return this.on(name, once, context); }, off: function(name, callback, context) { var retain, ev, events, names, i, l, j, k; if (!this._events || !eventsApi(this, 'off', name, [callback, context])) return this; if (!name && !callback && !context) { this._events = void 0; return this; } names = name ? [name] : _.keys(this._events); for (i = 0, l = names.length; i < l; i++) { name = names[i]; if (events = this._events[name]) { this._events[name] = retain = []; if (callback || context) { for (j = 0, k = events.length; j < k; j++) { ev = events[j]; if ((callback && callback !== ev.callback && callback !== ev.callback._callback) || (context && context !== ev.context)) { retain.push(ev); } } } if (!retain.length) delete this._events[name]; } } return this; }, trigger: function(name, options) { var args = [options], events, allEvents; if (!this._events) { return this; } if (!eventsApi(this, 'trigger', name, args)) { return this; } events = this._events[name]; allEvents = this._events.all; if (events) { triggerEvents(events, args); } if (allEvents) { triggerEvents(allEvents, args); } return this; }, stopListening: function(obj, name, callback) { var listeningTo = this._listeningTo; if (!listeningTo) return this; var remove = !name && !callback; if (!callback && typeof name === 'object') callback = this; if (obj)(listeningTo = {})[obj._listenId] = obj; for (var id in listeningTo) { obj = listeningTo[id]; obj.off(name, callback, this); if (remove || _.isEmpty(obj._events)) delete this._listeningTo[id]; } return this; } }; // Build class types while (len--) { var t = types[len]; class_types["[object " + t + "]"] = t.toLowerCase(); } // Public Object return { isWindow: isWindow, type: type, extend: extend, isPlainObject: isPlainObject, isArray: isArray, isFunction: isFunction, addEvent: addEvent, calcAverage: calcAverage, triggerEvent: triggerEvent, Events: Events }; ////////////////////////////// }();