UNPKG

@zkochan/pnpm

Version:

A fast implementation of npm install

1,903 lines (1,577 loc) 57.6 kB
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ /* eslint-disable no-new */ var Pjax = require('pjax') var Nprogress = require('nprogress') var onmount = require('onmount') var each = require('dom101/each') var toggleClass = require('dom101/toggle-class') var ready = require('dom101/ready') var Scrolltrack = require('./scrolltrack') var Scrollclass = require('./scrollclass') /* * pjax/nprogress */ void (function () { ready(function () { new Pjax({ selectors: ['.body', '.toc-menu', 'title'], analytics: sendPageview }) }) ready(sendPageview) function sendPageview () { var title = document.querySelector('title') if (window.ga) { window.ga('send', 'pageview', { page: window.location.href, title: title && title.text }) } } document.addEventListener('pjax:send', function () { Nprogress.start() }) document.addEventListener('pjax:error', function () { Nprogress.done() }) document.addEventListener('pjax:complete', function () { Nprogress.done() }) }()) /* * menu toggle */ onmount('.js-menu-toggle', function () { this.addEventListener('click', function () { toggleClass(document.body, '-menu-visible') }) }) /* * onmount */ void (function () { ready(function () { onmount() }) document.addEventListener('pjax:complete', function () { onmount() }) }()) /* * scrolltrack */ void (function () { var st = new Scrolltrack({ menu: '.toc-menu', selector: 'h2, h3', onupdate: function (active, last) { var menu = document.querySelector('.toc-menu') var link = menu.querySelector('.link.-active, .link.-notactive') toggleClass(link, '-active', !active) toggleClass(link, '-notactive', active) } }) document.addEventListener('pjax:complete', function () { st.update() }) ready(function () { st.update() }) }()) void (function () { onmount('.footer-nav', function (b) { b.sc = Scrollclass(this, { className: '-expanded', onscroll: function (y) { return this.maxScroll - y < 88 } }) }, function (b) { b.sc.destroy() }) }()) void (function () { onmount('.header-nav', function (b) { b.sc = Scrollclass(this, { className: '-expanded', onscroll: function (y) { return y < 40 } }) }, function (b) { b.sc.destroy() }) }()) },{"./scrollclass":2,"./scrolltrack":3,"dom101/each":8,"dom101/ready":10,"dom101/toggle-class":13,"nprogress":14,"onmount":15,"pjax":16}],2:[function(require,module,exports){ var debounce = require('debounce') var documentHeight = require('dom101/document-height') var toggleClass = require('dom101/toggle-class') var scrollTop = require('dom101/scroll-top') /** * Listens for scrolling. * Available options: * * * `parent` (Element) — the parent to listen for scroll events to. Defaults * to `document.` * * `className` (String) — classname to apply to the function. * * `onresize` (Function) — callback to run when the window resizes. Use * this to cache metrics. * * `onscroll` (Function) — callback to run when scrolling. When this returns * true, `className` will be applied; if false, it'll be removed. */ function Scrollclass (el, options) { if (!(this instanceof Scrollclass)) return new Scrollclass(el, options) if (!options) options = {} this.el = q(el) this.parent = q(options.parent || document) this.className = options.className || 'active' this.onresize = (options.onresize || noop).bind(this) this.onscroll = (options.onscroll || noop).bind(this) this._onscroll = debounce(this.poll.bind(this), 5) this._onresize = debounce(this.update.bind(this), 5) this.listen() } /** * Fires event listeners. */ Scrollclass.prototype.listen = function () { window.addEventListener('resize', this._onresize) window.addEventListener('resize', this._onscroll) document.addEventListener('load', this._onresize, true) // image events document.addEventListener('load', this._onscroll, true) this.parent.addEventListener('scroll', this._onscroll) this._onresize() this._onscroll() } /** * Destroys all event listeners. */ Scrollclass.prototype.destroy = function () { window.removeEventListener('resize', this._onresize) window.removeEventListener('resize', this._onscroll) document.removeEventListener('load', this._onresize, true) document.removeEventListener('load', this._onscroll, true) this.parent.removeEventListener('scroll', this._onscroll) } /** * Internal: Updates data on window resize. This sets some useful stuff that * can be used by the `onscroll` handler. */ Scrollclass.prototype.update = function () { this.documentHeight = documentHeight() this.winHeight = window.innerHeight this.maxScroll = this.documentHeight - this.winHeight this.onresize() } /** * Internal: scroll handler. */ Scrollclass.prototype.poll = function () { var result = this.onscroll(scrollTop()) toggleClass(this.el, this.className, result) } function noop () {} /** * Internal: helper to normalize between CSS selectors, DOM elements and * jQuery objects. */ function q (el) { if (typeof el === 'string') return document.querySelector(el) else if (typeof el === 'object' && el[0]) return el[0] else return el } module.exports = Scrollclass },{"debounce":4,"dom101/document-height":7,"dom101/scroll-top":12,"dom101/toggle-class":13}],3:[function(require,module,exports){ var toggleClass = require('dom101/toggle-class') var scrollTop = require('dom101/scroll-top') var documentHeight = require('dom101/document-height') var debounce = require('debounce') var each = require('dom101/each') /** * Tracks scrolling. Options: * * - `selectors` (String) * - `parent` (String) - where headings are. defaults to `document` * - `menu` (String | Element) - where links are. * - `scrollParent` (String | Element) - where to listen for scroll events. * - `onupdate` (Function) - callback to invoke when links change. */ function Scrolltrack (options) { if (!(this instanceof Scrolltrack)) return new Scrolltrack(options) if (!options) options = {} this.selector = options.selector || 'h1, h2, h3, h4, h5, h6' this.parent = options.parent || document this.onupdate = options.onupdate || function () {} this.menu = options.menu || document this.scrollParent = options.scrollParent || document this.offsetPercent = options.offsetPercent || 0.1 this.listener = debounce(this.onScroll, 5).bind(this) this.update = debounce(this._update, 20).bind(this) this.active = undefined this.index = [] this.listen() this.update() } /** * Internal: Attaches event listeners. * No need to call this; the constructor already does this. */ Scrolltrack.prototype.listen = function () { q(this.scrollParent).addEventListener('scroll', this.listener) document.addEventListener('load', this.update, true) // image events document.addEventListener('load', this.listener, true) window.addEventListener('resize', this.update) window.addEventListener('resize', this.listener) } /** * Stops listening for events. */ Scrolltrack.prototype.destroy = function () { q(this.scrollParent).removeEventListener('scroll', this.listener) document.removeEventListener('load', this.update, true) document.removeEventListener('load', this.listener, true) window.removeEventListener('resize', this.update) window.removeEventListener('resize', this.listener) } /** * Internal: Updates the index of the headings and links. * Used by `update()`. */ Scrolltrack.prototype.reindex = function () { var headings = this.parent.querySelectorAll(this.selector) var index = this.index = [] var ids = {} var menu = q(this.menu) each(headings, function (heading) { var rect = heading.getBoundingClientRect() var id = heading.getAttribute('id') if (!ids[id]) ids[id] = 0 else ids[id]++ var links = menu.querySelectorAll('[href=' + JSON.stringify('#' + id) + ']') index.push({ el: heading, id: id, link: links[ids[id]], top: rect.top + scrollTop() }) }) this.metrics = { windowHeight: window.innerHeight, documentHeight: documentHeight() } } /** * update : update() * Updates indices. Call this when the DOM changes. */ Scrolltrack.prototype._update = function () { this.reindex() this.onScroll() } /** * Internal: check for updates when scrolling. This is attached to the * document's scroll event. */ Scrolltrack.prototype.onScroll = function () { var y = this.scrollTop() var active each(this.index, function (heading) { if (heading.top < y) active = heading }) if (active !== this.active) { var last = this.active this.active = active this.follow(active, last) this.onupdate(active, last) } } /** * Returns the scroll position. This also takes care of scaling it to go all * the way to the bottom. */ Scrolltrack.prototype.scrollTop = function () { var y = scrollTop() var offset = 0 var k = this.offsetPercent if (this.metrics) { var screen = this.metrics.windowHeight var maxY = this.metrics.documentHeight - screen var fold = maxY - screen * 1.2 if (y > fold) { var lastPercent = (y - fold) / screen offset = screen * (k + (1 - k) * lastPercent) } else { offset = screen * k } } return y + offset } /** * Updates the selected link. */ Scrolltrack.prototype.follow = function (heading, last) { if (this.lastlink) { toggleClass(this.lastlink, '-active', false) this.lastlink = null } if (heading && heading.link) { toggleClass(heading.link, '-active', true) this.lastlink = heading.link } } /** * Internal: helper to normalize between CSS selectors, DOM elements and * jQuery objects. */ function q (el) { if (typeof el === 'string') return document.querySelector(el) else if (typeof el === 'object' && el[0]) return el[0] else return el } module.exports = Scrolltrack },{"debounce":4,"dom101/document-height":7,"dom101/each":8,"dom101/scroll-top":12,"dom101/toggle-class":13}],4:[function(require,module,exports){ /** * Module dependencies. */ var now = require('date-now'); /** * Returns a function, that, as long as it continues to be invoked, will not * be triggered. The function will be called after it stops being called for * N milliseconds. If `immediate` is passed, trigger the function on the * leading edge, instead of the trailing. * * @source underscore.js * @see http://unscriptable.com/2009/03/20/debouncing-javascript-methods/ * @param {Function} function to wrap * @param {Number} timeout in ms (`100`) * @param {Boolean} whether to execute at the beginning (`false`) * @api public */ module.exports = function debounce(func, wait, immediate){ var timeout, args, context, timestamp, result; if (null == wait) wait = 100; function later() { var last = now() - timestamp; if (last < wait && last > 0) { timeout = setTimeout(later, wait - last); } else { timeout = null; if (!immediate) { result = func.apply(context, args); if (!timeout) context = args = null; } } }; return function debounced() { context = this; args = arguments; timestamp = now(); var callNow = immediate && !timeout; if (!timeout) timeout = setTimeout(later, wait); if (callNow) { result = func.apply(context, args); context = args = null; } return result; }; }; },{"date-now":5}],5:[function(require,module,exports){ module.exports = Date.now || now function now() { return new Date().getTime() } },{}],6:[function(require,module,exports){ /** * addClass : addClass(el, className) * Adds a class name to an element. Compare with `$.fn.addClass`. * * var addClass = require('dom101/add-class'); * * addClass(el, 'active'); */ function addClass (el, className) { if (el.classList) { el.classList.add(className); } else { el.className += ' ' + className; } } module.exports = addClass; },{}],7:[function(require,module,exports){ /** * documentHeight : documentHeight() * Returns the height of the document. * Compare with jQuery's `$(document).height()`. * * var documentHeight = require('dom101/document-height'); * * var height = documentHeight(); */ function documentHeight () { return Math.max( document.documentElement.clientHeight || 0, document.body.scrollHeight || 0, document.documentElement.scrollHeight || 0, document.body.offsetHeight || 0, document.documentElement.offsetHeight || 0); } module.exports = documentHeight; },{}],8:[function(require,module,exports){ /** * each : each(list, fn) * Iterates through `list` (an array or an object). This is useful when dealing * with NodeLists like `document.querySelectorAll`. * * var each = require('dom101/each'); * var qa = require('dom101/query-selector-all'); * * each(qa('.button'), function (el) { * addClass('el', 'selected'); * }); */ function each (list, fn) { var i; var len = list.length; var idx; if (typeof len === 'number') { for (i = 0; i < len; i++) { fn(list[i], i); } } else { idx = 0; for (i in list) { if (list.hasOwnProperty(i)) { fn(list[i], i, idx++); } } } return list; } module.exports = each; },{}],9:[function(require,module,exports){ /** * hasClass : hasClass(el, className) * Checks if an element has a given class name. * * var hasClass = require('dom101/has-class'); * * el.className = 'selected active'; * hasClass(el, 'active') //=> true */ function hasClass (el, className) { if (el.classList) { return el.classList.contains(className); } else { return new RegExp('(^| )' + className + '( |$)', 'gi').test(el.className); } } module.exports = hasClass; },{}],10:[function(require,module,exports){ /** * ready : ready(fn) * Executes `fn` when the DOM is ready. * * var ready = require('dom101/ready'); * * ready(function () { * ... * }); */ function ready (fn) { if (document.addEventListener) { document.addEventListener('DOMContentLoaded', fn); } else { document.attachEvent('onreadystatechange', function () { if (document.readyState === 'interactive') fn(); }); } } module.exports = ready; },{}],11:[function(require,module,exports){ /** * removeClass : removeClass(el, className) * Removes a classname. * * var removeClass = require('dom101/remove-class'); * * el.className = 'selected active'; * removeClass(el, 'active'); * * el.className * => "selected" */ function removeClass (el, className) { if (el.classList) { el.classList.remove(className); } else { var expr = new RegExp('(^|\\b)' + className.split(' ').join('|') + '(\\b|$)', 'gi'); el.className = el.className.replace(expr, ' '); } } module.exports = removeClass; },{}],12:[function(require,module,exports){ /** * scrollTop : scrollTop() * Returns the scroll top value. * * var scrollTop = require('dom101/scroll-top'); * alert(scrollTop()); */ function scrollTop () { if (window.pageYOffset) return window.pageYOffset; return document.documentElement.clientHeight ? document.documentElement.scrollTop : document.body.scrollTop; } // Taken from https://github.com/yields/scrolltop/blob/master/index.js module.exports = scrollTop; },{}],13:[function(require,module,exports){ /** * toggleClass : toggleClass(el, className, [value]) * Adds or removes a class name to an element. If `value` is provided, * this will add the class if it's `true` or remove if it's `false`. * Compare with `$.fn.toggleClass`. * * var toggleClass = require('dom101/toggle-class'); * * // toggles on or off: * toggleClass(el, 'active'); * * // with a value: * var isSelected = true; * toggleClass(el, 'selected', isSelected); */ var addClass = require('./add-class'); var removeClass = require('./remove-class'); var hasClass = require('./has-class'); function toggleClass (el, className, value) { if (typeof value === 'undefined') { value = !hasClass(el, className); } return value ? addClass(el, className) : removeClass(el, className); } module.exports = toggleClass; },{"./add-class":6,"./has-class":9,"./remove-class":11}],14:[function(require,module,exports){ /* NProgress, (c) 2013, 2014 Rico Sta. Cruz - http://ricostacruz.com/nprogress * @license MIT */ ;(function(root, factory) { if (typeof define === 'function' && define.amd) { define(factory); } else if (typeof exports === 'object') { module.exports = factory(); } else { root.NProgress = factory(); } })(this, function() { var NProgress = {}; NProgress.version = '0.2.0'; var Settings = NProgress.settings = { minimum: 0.08, easing: 'ease', positionUsing: '', speed: 200, trickle: true, trickleRate: 0.02, trickleSpeed: 800, showSpinner: true, barSelector: '[role="bar"]', spinnerSelector: '[role="spinner"]', parent: 'body', template: '<div class="bar" role="bar"><div class="peg"></div></div><div class="spinner" role="spinner"><div class="spinner-icon"></div></div>' }; /** * Updates configuration. * * NProgress.configure({ * minimum: 0.1 * }); */ NProgress.configure = function(options) { var key, value; for (key in options) { value = options[key]; if (value !== undefined && options.hasOwnProperty(key)) Settings[key] = value; } return this; }; /** * Last number. */ NProgress.status = null; /** * Sets the progress bar status, where `n` is a number from `0.0` to `1.0`. * * NProgress.set(0.4); * NProgress.set(1.0); */ NProgress.set = function(n) { var started = NProgress.isStarted(); n = clamp(n, Settings.minimum, 1); NProgress.status = (n === 1 ? null : n); var progress = NProgress.render(!started), bar = progress.querySelector(Settings.barSelector), speed = Settings.speed, ease = Settings.easing; progress.offsetWidth; /* Repaint */ queue(function(next) { // Set positionUsing if it hasn't already been set if (Settings.positionUsing === '') Settings.positionUsing = NProgress.getPositioningCSS(); // Add transition css(bar, barPositionCSS(n, speed, ease)); if (n === 1) { // Fade out css(progress, { transition: 'none', opacity: 1 }); progress.offsetWidth; /* Repaint */ setTimeout(function() { css(progress, { transition: 'all ' + speed + 'ms linear', opacity: 0 }); setTimeout(function() { NProgress.remove(); next(); }, speed); }, speed); } else { setTimeout(next, speed); } }); return this; }; NProgress.isStarted = function() { return typeof NProgress.status === 'number'; }; /** * Shows the progress bar. * This is the same as setting the status to 0%, except that it doesn't go backwards. * * NProgress.start(); * */ NProgress.start = function() { if (!NProgress.status) NProgress.set(0); var work = function() { setTimeout(function() { if (!NProgress.status) return; NProgress.trickle(); work(); }, Settings.trickleSpeed); }; if (Settings.trickle) work(); return this; }; /** * Hides the progress bar. * This is the *sort of* the same as setting the status to 100%, with the * difference being `done()` makes some placebo effect of some realistic motion. * * NProgress.done(); * * If `true` is passed, it will show the progress bar even if its hidden. * * NProgress.done(true); */ NProgress.done = function(force) { if (!force && !NProgress.status) return this; return NProgress.inc(0.3 + 0.5 * Math.random()).set(1); }; /** * Increments by a random amount. */ NProgress.inc = function(amount) { var n = NProgress.status; if (!n) { return NProgress.start(); } else { if (typeof amount !== 'number') { amount = (1 - n) * clamp(Math.random() * n, 0.1, 0.95); } n = clamp(n + amount, 0, 0.994); return NProgress.set(n); } }; NProgress.trickle = function() { return NProgress.inc(Math.random() * Settings.trickleRate); }; /** * Waits for all supplied jQuery promises and * increases the progress as the promises resolve. * * @param $promise jQUery Promise */ (function() { var initial = 0, current = 0; NProgress.promise = function($promise) { if (!$promise || $promise.state() === "resolved") { return this; } if (current === 0) { NProgress.start(); } initial++; current++; $promise.always(function() { current--; if (current === 0) { initial = 0; NProgress.done(); } else { NProgress.set((initial - current) / initial); } }); return this; }; })(); /** * (Internal) renders the progress bar markup based on the `template` * setting. */ NProgress.render = function(fromStart) { if (NProgress.isRendered()) return document.getElementById('nprogress'); addClass(document.documentElement, 'nprogress-busy'); var progress = document.createElement('div'); progress.id = 'nprogress'; progress.innerHTML = Settings.template; var bar = progress.querySelector(Settings.barSelector), perc = fromStart ? '-100' : toBarPerc(NProgress.status || 0), parent = document.querySelector(Settings.parent), spinner; css(bar, { transition: 'all 0 linear', transform: 'translate3d(' + perc + '%,0,0)' }); if (!Settings.showSpinner) { spinner = progress.querySelector(Settings.spinnerSelector); spinner && removeElement(spinner); } if (parent != document.body) { addClass(parent, 'nprogress-custom-parent'); } parent.appendChild(progress); return progress; }; /** * Removes the element. Opposite of render(). */ NProgress.remove = function() { removeClass(document.documentElement, 'nprogress-busy'); removeClass(document.querySelector(Settings.parent), 'nprogress-custom-parent'); var progress = document.getElementById('nprogress'); progress && removeElement(progress); }; /** * Checks if the progress bar is rendered. */ NProgress.isRendered = function() { return !!document.getElementById('nprogress'); }; /** * Determine which positioning CSS rule to use. */ NProgress.getPositioningCSS = function() { // Sniff on document.body.style var bodyStyle = document.body.style; // Sniff prefixes var vendorPrefix = ('WebkitTransform' in bodyStyle) ? 'Webkit' : ('MozTransform' in bodyStyle) ? 'Moz' : ('msTransform' in bodyStyle) ? 'ms' : ('OTransform' in bodyStyle) ? 'O' : ''; if (vendorPrefix + 'Perspective' in bodyStyle) { // Modern browsers with 3D support, e.g. Webkit, IE10 return 'translate3d'; } else if (vendorPrefix + 'Transform' in bodyStyle) { // Browsers without 3D support, e.g. IE9 return 'translate'; } else { // Browsers without translate() support, e.g. IE7-8 return 'margin'; } }; /** * Helpers */ function clamp(n, min, max) { if (n < min) return min; if (n > max) return max; return n; } /** * (Internal) converts a percentage (`0..1`) to a bar translateX * percentage (`-100%..0%`). */ function toBarPerc(n) { return (-1 + n) * 100; } /** * (Internal) returns the correct CSS for changing the bar's * position given an n percentage, and speed and ease from Settings */ function barPositionCSS(n, speed, ease) { var barCSS; if (Settings.positionUsing === 'translate3d') { barCSS = { transform: 'translate3d('+toBarPerc(n)+'%,0,0)' }; } else if (Settings.positionUsing === 'translate') { barCSS = { transform: 'translate('+toBarPerc(n)+'%,0)' }; } else { barCSS = { 'margin-left': toBarPerc(n)+'%' }; } barCSS.transition = 'all '+speed+'ms '+ease; return barCSS; } /** * (Internal) Queues a function to be executed. */ var queue = (function() { var pending = []; function next() { var fn = pending.shift(); if (fn) { fn(next); } } return function(fn) { pending.push(fn); if (pending.length == 1) next(); }; })(); /** * (Internal) Applies css properties to an element, similar to the jQuery * css method. * * While this helper does assist with vendor prefixed property names, it * does not perform any manipulation of values prior to setting styles. */ var css = (function() { var cssPrefixes = [ 'Webkit', 'O', 'Moz', 'ms' ], cssProps = {}; function camelCase(string) { return string.replace(/^-ms-/, 'ms-').replace(/-([\da-z])/gi, function(match, letter) { return letter.toUpperCase(); }); } function getVendorProp(name) { var style = document.body.style; if (name in style) return name; var i = cssPrefixes.length, capName = name.charAt(0).toUpperCase() + name.slice(1), vendorName; while (i--) { vendorName = cssPrefixes[i] + capName; if (vendorName in style) return vendorName; } return name; } function getStyleProp(name) { name = camelCase(name); return cssProps[name] || (cssProps[name] = getVendorProp(name)); } function applyCss(element, prop, value) { prop = getStyleProp(prop); element.style[prop] = value; } return function(element, properties) { var args = arguments, prop, value; if (args.length == 2) { for (prop in properties) { value = properties[prop]; if (value !== undefined && properties.hasOwnProperty(prop)) applyCss(element, prop, value); } } else { applyCss(element, args[1], args[2]); } } })(); /** * (Internal) Determines if an element or space separated list of class names contains a class name. */ function hasClass(element, name) { var list = typeof element == 'string' ? element : classList(element); return list.indexOf(' ' + name + ' ') >= 0; } /** * (Internal) Adds a class to an element. */ function addClass(element, name) { var oldList = classList(element), newList = oldList + name; if (hasClass(oldList, name)) return; // Trim the opening space. element.className = newList.substring(1); } /** * (Internal) Removes a class from an element. */ function removeClass(element, name) { var oldList = classList(element), newList; if (!hasClass(element, name)) return; // Replace the class name. newList = oldList.replace(' ' + name + ' ', ' '); // Trim the opening and closing spaces. element.className = newList.substring(1, newList.length - 1); } /** * (Internal) Gets a space separated list of the class names on the element. * The list is wrapped with a single space on each end to facilitate finding * matches within the list. */ function classList(element) { return (' ' + (element.className || '') + ' ').replace(/\s+/gi, ' '); } /** * (Internal) Removes an element from the DOM. */ function removeElement(element) { element && element.parentNode && element.parentNode.removeChild(element); } return NProgress; }); },{}],15:[function(require,module,exports){ /* global define */ void (function (root, factory) { if (typeof define === 'function' && define.amd) define(factory) else if (typeof exports === 'object') module.exports = factory() else { if (window.jQuery) window.jQuery.onmount = factory() else root.onmount = factory() } }(this, function ($) { /* * Internal: Registry. */ var handlers, behaviors, selectors, log /* * Internal: IDs for auto-incrementing. */ var bid = 0 /* behavior ID */ var cid = 0 /* component ID */ /** * (Module) Adds a behavior, or triggers behaviors. * * When no parameters are passed, it triggers all behaviors. When one * parameter is passed, it triggers the given behavior. Otherwise, it adds a * behavior. * * // define a behavior * $.onmount('.select-box', function () { * $(this).on('...') * }) * * // define a behavior with exit * $.onmount('.select-box', function () { * $(document).on('...') * }, function () { * $(document).off('...') * }) * * // retrigger a onmount * $.onmount('.select-box') * * // retriggers all behaviors * $.onmount() */ function onmount (selector, init, exit) { if (arguments.length === 0 || isjQuery(selector) || isEvent(selector)) { // onmount() - trigger all behaviors. Also account for cases such as // $($.onmount), where it's triggered with a jQuery event object. onmount.poll() } else if (arguments.length === 1) { // onmount(selector) - trigger for a given selector. onmount.poll(selector) } else { // onmount(sel, fn, [fn]) - register a new behavior. var be = new Behavior(selector, init, exit) behaviors.push(be) be.register() } return this } /* * Use jQuery (or a jQuery-like) when available. This will allow * the use of jQuery selectors. */ onmount.$ = window.jQuery || window.Zepto || window.Ender /* * Detect MutationObserver support for `onmount.observe()`. * You may even add a polyfill here via * `onmount.MutationObserver = require('mutation-observer')`. */ onmount.MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver /** * Set this to true if you want to see debug messages. */ onmount.debug = false /** * Internal: triggers behaviors for a selector or for all. * * onmount.poll() * onmount.poll('.js-button') */ onmount.poll = function poll (selector) { if (selector) selector = onmount.selectify(selector) var list = (selector ? selectors[selector] : handlers) || [] each(list, function (item) { item() }) } /** * Observes automatically using MutationObserver events. * * onmount.observe() */ onmount.observe = function observe () { var MutationObserver = onmount.MutationObserver if (typeof MutationObserver === 'undefined') return var obs = new MutationObserver(function (mutations) { each(behaviors, function (be) { each(mutations, function (mutation) { each(mutation.addedNodes, function (el) { if (matches(el, be.selector)) be.visitEnter(el) }) each(mutation.removedNodes, function (el) { if (matches(el, be.selector)) be.visitExit(el) }) }) }) }) obs.observe(document, { subtree: true, childList: true }) onmount.observer = obs // trigger everything before going onmount() return true } /** * Turns off observation first issued by `onmount.observe()`. */ onmount.unobserve = function unobserve () { if (!this.observer) return this.observer.disconnect() delete this.observer } /** * Clears all behaviors. Useful for tests. * This will NOT call exit handlers. */ onmount.reset = function reset () { handlers = onmount.handlers = [] selectors = onmount.selectors = {} behaviors = onmount.behaviors = [] } /** * Internal: Converts `@role` to `[role~="role"]` if needed. You can override * this by reimplementing `onmount.selectify`. * * selectify('@hi') //=> '[role="hi"]' * selectify('.btn') //=> '.btn' */ onmount.selectify = function selectify (selector) { if (selector[0] === '@') { return '[role~="' + selector.substr(1).replace(/"/g, '\\"') + '"]' } return selector } /** * Internal: behavior class */ function Behavior (selector, init, exit) { this.id = 'b' + bid++ this.init = init this.exit = exit this.selector = onmount.selectify(selector) this.loaded = [] // keep track of dom elements loaded for this behavior this.key = '__onmount:' + bid // leave the state in el['__onmount:12'] } /** * Internal: Register this behavior */ Behavior.prototype.register = function () { var be = this var loaded = this.loaded var selector = this.selector register(selector, function () { // clean up old ones and initialize new ones each(loaded, function (element, i) { be.visitExit(element, i) }) query(selector, function () { be.visitEnter(this) }) }) } /** * Internal: visits the element `el` and turns it on if applicable */ Behavior.prototype.visitEnter = function (el) { if (el[this.key]) return var options = { id: 'c' + cid, selector: this.selector } if (this.init.call(el, options) !== false) { if (onmount.debug) log('enter', this.selector, el) el[this.key] = options this.loaded.push(el) cid++ } } /** * Internal: visits the element `el` and sees if it needs its exit handler * called */ Behavior.prototype.visitExit = function (el, i) { if (el && !isAttached(el)) { if (typeof i === 'undefined') i = this.loaded.indexOf(el) this.loaded[i] = undefined if (this.exit && this.exit.call(el, el[this.key]) !== false) { if (onmount.debug) log('exit', this.selector, el) delete el[this.key] } } } /** * Internal: check if an element is still attached to its document. */ function isAttached (el) { while (el) { if (el === document.documentElement) return true el = el.parentElement } } /** * Internal: reimplementation of `$('...').each()`. If jQuery is available, * use it (I guess to preserve IE compatibility and to enable special jQuery * attribute selectors). */ function query (selector, fn) { if (onmount.$) return onmount.$(selector).each(fn) var list = document.querySelectorAll(selector) for (var i = 0, len = list.length; i < len; i++) { fn.apply(list[i]) } } /** * Internal: registers a behavior handler for a selector. */ function register (selector, fn) { if (!selectors[selector]) selectors[selector] = [] selectors[selector].push(fn) handlers.push(fn) } /** * Checks if a given element `el` matches `selector`. * Compare with [$.fn.is](http://api.jquery.com/is/). * * var matches = require('dom101/matches'); * * matches(button, ':focus'); */ function matches (el, selector) { var _matches = el.matches || el.matchesSelector || el.msMatchesSelector || el.mozMatchesSelector || el.webkitMatchesSelector || el.oMatchesSelector if (onmount.$) { return onmount.$(el).is(selector) } else if (_matches) { return _matches.call(el, selector) } else if (el.parentNode) { // IE8 and below var nodes = el.parentNode.querySelectorAll(selector) for (var i = nodes.length; i--; 0) { if (nodes[i] === el) return true } return false } } /** * Iterates through `list` (an array or an object). This is useful when dealing * with NodeLists like `document.querySelectorAll`. * * var each = require('dom101/each'); * var qa = require('dom101/query-selector-all'); * * each(qa('.button'), function (el) { * addClass('el', 'selected'); * }); */ function each (list, fn) { var i var len = list.length if (len === +len) { for (i = 0; i < len; i++) { fn(list[i], i) } } else { for (i in list) { if (list.hasOwnProperty(i)) fn(list[i], i) } } return list } /** * Internal: Check if a given object is jQuery */ function isjQuery ($) { return typeof $ === 'function' && $.fn && $.noConflict } function isEvent (e) { return typeof e === 'object' && e.target } /** * Internal: logging */ var styles = { enter: 'background-color:#dfd;font-weight:bold;color:#141', exit: 'background-color:#fdd;font-weight:bold;color:#411' } if (~navigator.userAgent.indexOf('Mozilla')) { log = function (type, selector, el) { console.log('%c %s ', styles[type], selector, el) } } else { log = function (type, selector, el) { console.log('(onmount)', type, selector) } } /* * Export */ onmount.reset() return onmount })) },{}],16:[function(require,module,exports){ ;(function(root, factory) { if (typeof exports === "object") { // CommonJS module.exports = factory() } else if (typeof define === "function" && define.amd) { // AMD define([], factory) } else { // Global Variables root.Pjax = factory() } }(this, function() { "use strict"; function newUid() { return (new Date().getTime()) } var Pjax = function(options) { this.firstrun = true this.options = options this.options.elements = this.options.elements || "a[href], form[action]" this.options.selectors = this.options.selectors || ["title", ".js-Pjax"] this.options.switches = this.options.switches || {} this.options.switchesOptions = this.options.switchesOptions || {} this.options.history = this.options.history || true this.options.currentUrlFullReload = this.options.currentUrlFullReload || false this.options.analytics = this.options.analytics || function(options) { // options.backward or options.foward can be true or undefined // by default, we do track back/foward hit // https://productforums.google.com/forum/#!topic/analytics/WVwMDjLhXYk if (window._gaq) { _gaq.push(["_trackPageview"]) } if (window.ga) { ga("send", "pageview", {"page": options.url, "title": options.title}) } } this.options.scrollTo = this.options.scrollTo || 0 this.options.debug = this.options.debug || false this.maxUid = this.lastUid = newUid() // we can’t replace body.outerHTML or head.outerHTML // it create a bug where new body or new head are created in the dom // if you set head.outerHTML, a new body tag is appended, so the dom get 2 body // & it break the switchFallback which replace head & body if (!this.options.switches.head) { this.options.switches.head = this.switchElementsAlt } if (!this.options.switches.body) { this.options.switches.body = this.switchElementsAlt } this.log("Pjax options", this.options) if (typeof options.analytics !== "function") { options.analytics = function() {} } this.parseDOM(document) Pjax.on(window, "popstate", function(st) { if (st.state) { var opt = Pjax.clone(this.options) opt.url = st.state.url opt.title = st.state.title opt.history = false if (st.state.uid < this.lastUid) { opt.backward = true } else { opt.forward = true } this.lastUid = st.state.uid // @todo implement history cache here, based on uid this.loadUrl(st.state.url, opt) } }.bind(this)) } // make internal methods public Pjax.isSupported = function() { // Borrowed wholesale from https://github.com/defunkt/jquery-pjax return window.history && window.history.pushState && window.history.replaceState && // pushState isn’t reliable on iOS until 5. !navigator.userAgent.match(/((iPod|iPhone|iPad).+\bOS\s+[1-4]\D|WebApps\/.+CFNetwork)/) } Pjax.forEachEls = function(els, fn, context) { if (els instanceof HTMLCollection || els instanceof NodeList) { return Array.prototype.forEach.call(els, fn, context) } // assume simple dom element fn.call(context, els) } Pjax.on = function(els, events, listener, useCapture) { events = (typeof events === "string" ? events.split(" ") : events) events.forEach(function(e) { Pjax.forEachEls(els, function(el) { el.addEventListener(e, listener, useCapture) }) }, this) } Pjax.off = function(els, events, listener, useCapture) { events = (typeof events === "string" ? events.split(" ") : events) events.forEach(function(e) { Pjax.forEachEls(els, function(el) { el.removeEventListener(e, listener, useCapture) }) }, this) } Pjax.trigger = function(els, events) { events = (typeof events === "string" ? events.split(" ") : events) events.forEach(function(e) { var event if (document.createEvent) { event = document.createEvent("HTMLEvents") event.initEvent(e, true, true) } else { event = document.createEventObject() event.eventType = e } event.eventName = e if (document.createEvent) { Pjax.forEachEls(els, function(el) { el.dispatchEvent(event) }) } else { Pjax.forEachEls(els, function(el) { el.fireEvent("on" + event.eventType, event) }) } }, this) } Pjax.clone = function(obj) { if (null === obj || "object" != typeof obj) { return obj } var copy = obj.constructor() for (var attr in obj) { if (obj.hasOwnProperty(attr)) { copy[attr] = obj[attr] } } return copy } // Finds and executes scripts (used for newly added elements) // Needed since innerHTML does not run scripts Pjax.executeScripts = function(el) { // console.log("going to execute scripts for ", el) Pjax.forEachEls(el.querySelectorAll("script"), function(script) { if (!script.type || script.type.toLowerCase() === "text/javascript") { if (script.parentNode) { script.parentNode.removeChild(script) } Pjax.evalScript(script) } }) } Pjax.evalScript = function(el) { // console.log("going to execute script", el) var code = (el.text || el.textContent || el.innerHTML || "") , head = document.querySelector("head") || document.documentElement , script = document.createElement("script") if (code.match("document.write")) { if (console && console.log) { console.log("Script contains document.write. Can’t be executed correctly. Code skipped ", el) } return false } script.type = "text/javascript" try { script.appendChild(document.createTextNode(code)) } catch (e) { // old IEs have funky script nodes script.text = code } // execute head.insertBefore(script, head.firstChild) head.removeChild(script) // avoid pollution return true } Pjax.prototype = { log: function() { if (this.options.debug && console) { if (typeof console.log === "function") { console.log.apply(console, arguments); } // ie is weird else if (console.log) { console.log(arguments); } } } , getElements: function(el) { return el.querySelectorAll(this.options.elements) } , parseDOM: function(el) { Pjax.forEachEls(this.getElements(el), function(el) { switch (el.tagName.toLowerCase()) { case "a": this.attachLink(el) break case "form": // todo this.log("Pjax doesnt support <form> yet. TODO :)") break default: throw "Pjax can only be applied on <a> or <form> submit" } }, this) } , attachLink: function(el) { Pjax.on(el, "click", function(event) { //var el = event.currentTarget // Don’t break browser special behavior on links (like page in new window) if (event.which > 1 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) { return -1 } // Ignore external links. if (el.protocol !== window.location.protocol || el.host !== window.location.host) { return -2 } // Ignore anchors on the same page if (el.pathname === location.pathname && el.hash.length > 0) { return -3 } // Ignore anchors on the same page if (el.hash && el.href.replace(el.hash, "") === location.href.replace(location.hash, "")) { return -4 } // Ignore empty anchor "foo.html#" if (el.href === location.href + "#") { return -5 } event.preventDefault() if (this.options.currentUrlFullReload) { if (el.href === window.location.href) { window.location.reload() return -6 } } this.loadUrl(el.href, Pjax.clone(this.options)) }.bind(this)) Pjax.on(el, "keyup", function(event) { this.log("pjax todo") // todo handle a link hitted by keyboard (enter/space) when focus is on it }.bind(this)) } , forEachSelectors: function(cb, context, DOMcontext) { DOMcontext = DOMcontext || document this.options.selectors.forEach(function(selector) { Pjax.forEachEls(DOMcontext.querySelectorAll(selector), cb, context) }) } , switchSelectors: function(selectors, fromEl, toEl, options) { selectors.forEach(function(selector) { var newEls = fromEl.querySelectorAll(selector) var oldEls = toEl.querySelectorAll(selector) this.log("Pjax switch", selector, newEls, oldEls) if (newEls.length !== oldEls.length) { // Pjax.forEachEls(newEls, function(el) { // this.log("newEl", el, el.outerHTML) // }, this) // Pjax.forEachEls(oldEls, function(el) { // this.log("oldEl", el, el.outerHTML) // }, this) throw "DOM doesn’t look the same on new loaded page: ’" + selector + "’ - new " + newEls.length + ", old " + oldEls.length } Pjax.forEachEls(newEls, function(newEl, i) { var oldEl = oldEls[i] this.log("newEl", newEl, "oldEl", oldEl) if (this.options.switches[selector]) { this.options.switches[selector].bind(this)(oldEl, newEl, options, this.options.switchesOptions[selector]) } else { Pjax.switches.outerHTML.bind(this)(oldEl, newEl, options) } }, this) }, this) } // too much problem with the code below // + it’s too dangerous // , switchFallback: function(fromEl, toEl) { // this.switchSelectors(["head", "body"], fromEl, toEl) // // execute script when DOM is like it should be // Pjax.executeScripts(document.querySelector("head")) // Pjax.executeScripts(document.querySelector("body")) // } , latestChance: function(href) { window.location = href } , onSwitch: function() { Pjax.trigger(window, "resize scroll") } , loadContent: function(html, options) { var tmpEl = document.implementation.createHTMLDocument("") // parse HTML attributes to copy them // since we are forced to use documentElement.innerHTML (outerHTML can't be used for <html>) , htmlRegex = /<html[^>]+>/gi , htmlAttribsRegex = /\s?[a-z:]+(?:\=(?:\'|\")[^\'\">]+(?:\'|\"))*/gi , matches = html.match(htmlRegex) if (matches && matches.length) { matches = matches[0].match(htmlAttribsRegex) if (matches.length) { matches.shift() matches.forEach(function(htmlAttrib) { var attr = htmlAttrib.trim().split("=") tmpEl.documentElement.setAttribute(attr[0], attr[1].slice(1, -1)) }) } } tmpEl.documentElement.innerHTML = html this.log("load content", tmpEl.documentElement.attributes, tmpEl.documentElement.innerHTML.length) // try { this.switchSelectors(this.options.selectors, tmpEl, document, options) // FF bug: Won’t autofocus fields that are inserted via JS. // This behavior is incorrect. So if theres no current focus, autofocus // the last field. // // http://www.w3.org/html/wg/drafts/html/master/forms.html var autofocusEl = Array.prototype.slice.call(document.querySelectorAll("[autofocus]")).pop() if (autofocusEl && document.activeElement !== autofocusEl) { autofocusEl.focus(); } // execute scripts when DOM have been completely updated this.options.selectors.forEach(function(selector) { Pjax.forEachEls(document.querySelectorAll(selector), function(el) { Pjax.executeScripts(el) }) }) // } // catch(e) { // if (this.