UNPKG

metro4

Version:

The front-end framework for Build responsive, mobile-first projects on the web with the first front-end component library in Metro Style

730 lines (602 loc) 23.4 kB
(function() { 'use strict'; var $ = m4q; if (typeof m4q === 'undefined') { throw new Error('Metro UI requires m4q helper!'); } if (!('MutationObserver' in window)) { throw new Error('Metro UI requires MutationObserver!'); } var isTouch = (('ontouchstart' in window) || (navigator["MaxTouchPoints"] > 0) || (navigator["msMaxTouchPoints"] > 0)); var normalizeComponentName = function(name){ return typeof name !== "string" ? undefined : name.replace(/-/g, "").toLowerCase(); }; var Metro = { version: "5.0.0-rc0", build_time: "21.04.2024, 19:15:43", buildNumber: 0, isTouchable: isTouch, fullScreenEnabled: document.fullscreenEnabled, sheet: null, controlsPosition: { INSIDE: "inside", OUTSIDE: "outside" }, groupMode: { ONE: "one", MULTI: "multi" }, aspectRatio: { HD: "hd", SD: "sd", CINEMA: "cinema" }, fullScreenMode: { WINDOW: "window", DESKTOP: "desktop" }, position: { TOP: "top", BOTTOM: "bottom", LEFT: "left", RIGHT: "right", TOP_RIGHT: "top-right", TOP_LEFT: "top-left", BOTTOM_LEFT: "bottom-left", BOTTOM_RIGHT: "bottom-right", LEFT_BOTTOM: "left-bottom", LEFT_TOP: "left-top", RIGHT_TOP: "right-top", RIGHT_BOTTOM: "right-bottom" }, popoverEvents: { CLICK: "click", HOVER: "hover", FOCUS: "focus" }, stepperView: { SQUARE: "square", CYCLE: "cycle", DIAMOND: "diamond" }, listView: { LIST: "list", CONTENT: "content", ICONS: "icons", ICONS_MEDIUM: "icons-medium", ICONS_LARGE: "icons-large", TILES: "tiles", TABLE: "table" }, events: { click: 'click', start: isTouch ? 'touchstart' : 'mousedown', stop: isTouch ? 'touchend' : 'mouseup', move: isTouch ? 'touchmove' : 'mousemove', enter: isTouch ? 'touchstart' : 'mouseenter', startAll: 'mousedown touchstart', stopAll: 'mouseup touchend', moveAll: 'mousemove touchmove', leave: 'mouseleave', focus: 'focus', blur: 'blur', resize: 'resize', keyup: 'keyup', keydown: 'keydown', keypress: 'keypress', dblclick: 'dblclick', input: 'input', change: 'change', cut: 'cut', paste: 'paste', scroll: 'scroll', mousewheel: 'mousewheel', inputchange: "change input propertychange cut paste copy drop", dragstart: "dragstart", dragend: "dragend", dragenter: "dragenter", dragover: "dragover", dragleave: "dragleave", drop: 'drop', drag: 'drag' }, keyCode: { BACKSPACE: 8, TAB: 9, ENTER: 13, SHIFT: 16, CTRL: 17, ALT: 18, BREAK: 19, CAPS: 20, ESCAPE: 27, SPACE: 32, PAGEUP: 33, PAGEDOWN: 34, END: 35, HOME: 36, LEFT_ARROW: 37, UP_ARROW: 38, RIGHT_ARROW: 39, DOWN_ARROW: 40, COMMA: 188 }, media_queries: { FS: "(min-width: 0px)", XS: "(min-width: 360px)", SM: "(min-width: 576px)", MD: "(min-width: 768px)", LG: "(min-width: 992px)", XL: "(min-width: 1200px)", XXL: "(min-width: 1452px)" }, media_sizes: { FS: 0, XS: 360, SM: 576, LD: 640, MD: 768, LG: 992, XL: 1200, XXL: 1452 }, media_mode: { FS: "fs", XS: "xs", SM: "sm", MD: "md", LG: "lg", XL: "xl", XXL: "xxl" }, media_modes: ["fs","xs","sm","md","lg","xl","xxl"], actions: { REMOVE: 1, HIDE: 2 }, hotkeys: {}, locales: {}, utils: {}, colors: {}, dialog: null, pagination: null, md5: null, storage: null, export: null, animations: null, cookie: null, template: null, defaults: {}, about: function(){ var content = "<h3>About</h3>" + "<hr>" + "<div><b>Metro UI</b> - v" + Metro.version + "</div>" + "<div><b>M4Q</b> - " + m4q.version + "</div>"; Metro.infobox.create(content) }, info: function(){ console.info(`%c METRO UI %c v${Metro.version} %c ${Metro.build_time} `, "color: pink; font-weight: bold; background: #800000", "color: white; background: darkgreen", "color: white; background: #0080fe;") $.info() }, showCompileTime: function(){ return "" }, aboutDlg: function(){ alert("Metro UI - v" + Metro.version); }, ver: function(){ return Metro.version; }, build: function(){ return Metro.build; }, compile: function(){ return "" }, observe: function(){ var observer, observerCallback; var observerConfig = { childList: true, attributes: true, subtree: true }; observerCallback = function(mutations){ mutations.map(function(mutation){ if (mutation.type === 'attributes' && mutation.attributeName !== "data-role") { if (mutation.attributeName === 'data-hotkey') { Metro.initHotkeys([mutation.target], true); } else { var element = $(mutation.target); var mc = element.data('metroComponent'); var attr = mutation.attributeName, newValue = element.attr(attr), oldValue = mutation.oldValue; if (mc !== undefined) { element.fire("attr-change", { attr: attr, newValue: newValue, oldValue: oldValue, __this: element[0] }); $.each(mc, function(){ var plug = Metro.getPlugin(element, this); if (plug && typeof plug.changeAttribute === "function") { plug.changeAttribute(attr, newValue, oldValue); } }); } } } else if (mutation.type === 'childList' && mutation.addedNodes.length > 0) { var i, widgets = []; var $node, node, nodes = mutation.addedNodes; if (nodes.length) { for(i = 0; i < nodes.length; i++) { node = nodes[i]; $node = $(node); if ($node.attr("data-role") !== undefined) { widgets.push(node); } $.each($node.find("[data-role]"), function(){ var o = this; if (widgets.indexOf(o) !== -1) { return; } widgets.push(o); }); } if (widgets.length) Metro.initWidgets(widgets, "observe"); } } else { //console.log(mutation); } }); }; observer = new MutationObserver(observerCallback); observer.observe($("html")[0], observerConfig); }, init: function(){ var widgets = $("[data-role]"); var hotkeys = $("[data-hotkey]"); var html = $("html"); var that = this; if (window.METRO_BLUR_IMAGE) { html.addClass("use-blur-image"); } if (window.METRO_SHOW_ABOUT) Metro.info(true); if (isTouch === true) { html.addClass("metro-touch-device"); } else { html.addClass("metro-no-touch-device"); } Metro.sheet = this.utils.newCssSheet(); this.utils.addCssRule(Metro.sheet, "*, *::before, *::after", "box-sizing: border-box;"); window.METRO_MEDIA = []; $.each(Metro.media_queries, function(key, query){ if (that.utils.media(query)) { window.METRO_MEDIA.push(Metro.media_mode[key]); } }); Metro.observe(); Metro.initHotkeys(hotkeys); Metro.initWidgets(widgets, "init"); if (window.METRO_CLOAK_REMOVE !== "fade") { $(".m4-cloak").removeClass("m4-cloak"); $(window).fire("metro-initiated"); } else { $(".m4-cloak").animate({ draw: { opacity: 1 }, dur: 300, onDone: function(){ $(".m4-cloak").removeClass("m4-cloak"); $(window).fire("metro-initiated"); } }); } }, initHotkeys: function(hotkeys, redefine){ $.each(hotkeys, function(){ var element = $(this); var hotkey = element.attr('data-hotkey') ? element.attr('data-hotkey').toLowerCase() : false; var fn = element.attr('data-hotkey-func') ? element.attr('data-hotkey-func') : false; if (hotkey === false) { return; } if (element.data('hotKeyBonded') === true && redefine !== true) { return; } Metro.hotkeys[hotkey] = [this, fn]; element.data('hotKeyBonded', true); element.fire("hot-key-bonded", { __this: element[0], hotkey: hotkey, fn: fn }); }); }, initWidgets: function(widgets) { var that = this; $.each(widgets, function () { var $this = $(this), roles; if (!this.hasAttribute("data-role")) { return ; } roles = $this.attr('data-role').split(/\s*,\s*/); roles.map(function (func) { var $$ = that.utils.$(); var _func = normalizeComponentName(func); if ($$.fn[_func] !== undefined && $this.attr("data-role-"+_func) === undefined) { try { $$.fn[_func].call($this); $this.attr("data-role-"+_func, true); var mc = $this.data('metroComponent'); if (mc === undefined) { mc = [_func]; } else { mc.push(_func); } $this.data('metroComponent', mc); $this.fire("create", { __this: $this[0], name: _func }); $(document).fire("component-create", { element: $this[0], name: _func }); } catch (e) { console.error("Error creating component " + func + " for ", $this[0]); throw e; } } }); }); }, plugin: function(name, object){ var _name = normalizeComponentName(name); var register = function($){ $.fn[_name] = function( options ) { return this.each(function() { $.data( this, _name, Object.create(object).init(options, this )); }); }; } register(m4q); if (window.useJQuery) { register(jQuery); } }, pluginExists: function(name){ var $ = window.useJQuery ? jQuery : m4q; return typeof $.fn[normalizeComponentName(name)] === "function"; }, destroyPlugin: function(element, name){ var p, mc; var el = $(element); var _name = normalizeComponentName(name); p = Metro.getPlugin(el, _name); if (typeof p === 'undefined') { console.warn("Component "+name+" can not be destroyed: the element is not a Metro UI component."); return ; } if (typeof p['destroy'] !== 'function') { console.warn("Component "+name+" can not be destroyed: method destroy not found."); return ; } p['destroy'](); mc = el.data("metroComponent"); this.utils.arrayDelete(mc, _name); el.data("metroComponent", mc); $.removeData(el[0], _name); el.removeAttr("data-role-"+_name); }, destroyPluginAll: function(element){ var el = $(element); var mc = el.data("metroComponent"); if (mc !== undefined && mc.length > 0) $.each(mc, function(){ Metro.destroyPlugin(el[0], this); }); }, noop: function(){}, noop_true: function(){return true;}, noop_false: function(){return false;}, noop_arg: function(a){return a;}, requestFullScreen: function(element){ if (element["mozRequestFullScreen"]) { element["mozRequestFullScreen"](); } else if (element["webkitRequestFullScreen"]) { element["webkitRequestFullScreen"](); } else if (element["msRequestFullscreen"]) { element["msRequestFullscreen"](); } else { element.requestFullscreen().catch( function(err){ console.warn("Error attempting to enable full-screen mode: "+err.message+" "+err.name); }); } }, exitFullScreen: function(){ if (document["mozCancelFullScreen"]) { document["mozCancelFullScreen"](); } else if (document["webkitCancelFullScreen"]) { document["webkitCancelFullScreen"](); } else if (document["msExitFullscreen"]) { document["msExitFullscreen"](); } else { document.exitFullscreen().catch( function(err){ console.warn("Error attempting to disable full-screen mode: "+err.message+" "+err.name); }); } }, inFullScreen: function(){ var fsm = (document.fullscreenElement || document["webkitFullscreenElement"] || document["mozFullScreenElement"] || document["msFullscreenElement"]); return fsm !== undefined; }, $: function(){ return window.useJQuery ? jQuery : m4q; }, get$el: function(el){ return Metro.$()($(el)[0]); }, get$elements: function(el){ return Metro.$()($(el)); }, // TODO add if name is not defined, return one or array of plugins getPlugin: function(el, name){ var _name = normalizeComponentName(name); var $el = Metro.get$el(el); return $el.length ? $el.data(_name) : undefined; }, makePlugin: function(el, name, options){ var _name = normalizeComponentName(name); var $el = Metro.get$elements(el); return $el.length && typeof $el[_name] === "function" ? $el[_name](options) : undefined; }, Component: function(nameName, compObj){ var name = normalizeComponentName(nameName); var Utils = Metro.utils; var component = $.extend({name: name}, { _super: function(el, options, defaults, setup){ var self = this; this.elem = el; this.element = $(el); this.options = $.extend( {}, defaults, options ); this.component = this.elem; this._setOptionsFromDOM(); this._runtime(); if (setup && typeof setup === 'object') { $.each(setup, function(key, val){ self[key] = val; }) } this._createExec(); }, _setOptionsFromDOM: function(){ var element = this.element, o = this.options; $.each(element.data(), function(key, value){ if (key in o) { try { o[key] = JSON.parse(value); } catch (e) { o[key] = value; } } }); }, _runtime: function(){ var element = this.element, mc; var roles = (element.attr("data-role") || "").toArray(",").map(function(v){ return normalizeComponentName(v); }).filter(function(v){ return v.trim() !== ""; }); if (!element.attr('data-role-'+this.name)) { element.attr("data-role-"+this.name, true); if (roles.indexOf(this.name) === -1) { roles.push(this.name); element.attr("data-role", roles.join(",")); } mc = element.data('metroComponent'); if (mc === undefined) { mc = [this.name]; } else { mc.push(this.name); } element.data('metroComponent', mc); } }, _createExec: function(){ var that = this, timeout = this.options[this.name+'Deferred']; if (timeout) { setTimeout(function(){ that._create(); }, timeout) } else { that._create(); } }, _fireEvent: function(eventName, data, log, noFire, context = null){ var element = this.element, o = this.options; var _data; var event = $.camelCase(eventName).capitalize(); data = $.extend({}, data, {__this: element[0]}); _data = data ? Object.values(data) : {}; if (log) { console.warn(log); console.warn("Event: " + "on"+event); console.warn("Data: ", data); console.warn("Element: ", element[0]); } if (noFire !== true) element.fire(event.toLowerCase(), data); return Utils.exec(o["on"+event], _data, context ? context : element[0]); }, _fireEvents: function(events, data, log, noFire, context){ var that = this, _events; if (arguments.length === 0) { return ; } if (arguments.length === 1) { $.each(events, function () { var ev = this; that._fireEvent(ev.name, ev.data, ev.log, ev.noFire, context); }); return Utils.objectLength(events); } if (!Array.isArray(events) && typeof events !== "string") { return ; } _events = Array.isArray(events) ? events : events.toArray(","); $.each(_events, function(){ that._fireEvent(this, data, log, noFire, context); }); }, getComponent: function(){ return this.component; }, getComponentName: function(){ return this.name; } }, compObj); Metro.plugin(name, component); return component; }, fetch: { status: function(response){ return response.ok ? Promise.resolve(response) : Promise.reject(new Error(response.statusText)); }, json: function(response){ return response.json(); }, text: function(response){ return response.text(); }, form: function(response){ return response.formData(); }, blob: function(response){ return response.blob(); }, buffer: function(response){ return response.arrayBuffer(); } } }; $(window).on(Metro.events.resize, function(){ window.METRO_MEDIA = []; $.each(Metro.media_queries, function(key, query){ if (Metro.utils.media(query)) { window.METRO_MEDIA.push(Metro.media_mode[key]); } }); }); window.Metro = Metro; if (window.METRO_INIT === true) { $(function(){ Metro.init() }); } return Metro; }());