<commit_before>/*eslint dot-notation:0*/
import {Origin} from 'aurelia-metadata';
import {TemplateRegistryEntry, Loader} from 'aurelia-loader';
import {TextTemplateLoader} from './text-template-loader';
import {PLATFORM} from 'aurelia-pal';

function ensureOriginOnExports(executed, name) {
  let target = executed;
  let key;
  let exportedValue;

  if (target.__useDefault) {
    target = target['default'];
  }

  Origin.set(target, new Origin(name, 'default'));

  for (key in target) {
    exportedValue = target[key];

    if (typeof exportedValue === 'function') {
      Origin.set(exportedValue, new Origin(name, key));
    }
  }

  return executed;
}

/**
* Represents a template loader.
*/
interface TemplateLoader {
  /**
  * Loads a template.
  * @param loader The loader that is requesting the template load.
  * @param entry The TemplateRegistryEntry to load and populate with a template.
  * @return A promise which resolves when the TemplateRegistryEntry is loaded with a template.
  */
  loadTemplate(loader: Loader, entry: TemplateRegistryEntry): Promise<any>;
}

/**
* A default implementation of the Loader abstraction which works with SystemJS, RequireJS and Dojo Loader.
*/
export class DefaultLoader extends Loader {
  /**
  * The name of the underlying native loader plugin used to load text.
  */
  textPluginName: string = 'text';

  /**
  * Creates an instance of the DefaultLoader.
  */
  constructor() {
    super();

    this.moduleRegistry = Object.create(null);
    this.useTemplateLoader(new TextTemplateLoader());

    let that = this;

    this.addPlugin('template-registry-entry', {
      'fetch': function(address) {
        let entry = that.getOrCreateTemplateRegistryEntry(address);
        return entry.templateIsLoaded ? entry : that.templateLoader.loadTemplate(that, entry).then(x => entry);
      }
    });
  }

  /**
  * Instructs the loader to use a specific TemplateLoader instance for loading templates
  * @param templateLoader The instance of TemplateLoader to use for loading templates.
  */
  useTemplateLoader(templateLoader: TemplateLoader): void {
    this.templateLoader = templateLoader;
  }

  /**
  * Loads a collection of modules.
  * @param ids The set of module ids to load.
  * @return A Promise for an array of loaded modules.
  */
  loadAllModules(ids: string[]): Promise<any[]> {
    let loads = [];

    for (let i = 0, ii = ids.length; i < ii; ++i) {
      loads.push(this.loadModule(ids[i]));
    }

    return Promise.all(loads);
  }

  /**
  * Loads a template.
  * @param url The url of the template to load.
  * @return A Promise for a TemplateRegistryEntry containing the template.
  */
  loadTemplate(url: string): Promise<TemplateRegistryEntry> {
    return this._import(this.applyPluginToUrl(url, 'template-registry-entry'));
  }

  /**
  * Loads a text-based resource.
  * @param url The url of the text file to load.
  * @return A Promise for text content.
  */
  loadText(url: string): Promise<string> {
    return this._import(this.applyPluginToUrl(url, this.textPluginName)).then(textOrModule => {
      if (typeof textOrModule === 'string') {
        return textOrModule;
      }

      return textOrModule['default'];
    });
  }
}

PLATFORM.Loader = DefaultLoader;

if (!PLATFORM.global.System || !PLATFORM.global.System.import) {
  if (PLATFORM.global.requirejs && requirejs.s && requirejs.s.contexts && requirejs.s.contexts._ && requirejs.s.contexts._.defined) {
    PLATFORM.eachModule = function(callback) {
      let defined = requirejs.s.contexts._.defined;
      for (let key in defined) {
        try {
          if (callback(key, defined[key])) return;
        } catch (e) {}
      }
    };
  } else {
    PLATFORM.eachModule = function(callback) {};
  }

  DefaultLoader.prototype._import = function(moduleId) {
    return new Promise((resolve, reject) => {
      require([moduleId], resolve, reject);
    });
  };

  DefaultLoader.prototype.loadModule = function(id) {
    let existing = this.moduleRegistry[id];
    if (existing !== undefined) {
      return Promise.resolve(existing);
    }

    return new Promise((resolve, reject) => {
      require([id], m => {
        this.moduleRegistry[id] = m;
        resolve(ensureOriginOnExports(m, id));
      }, reject);
    });
  };

  DefaultLoader.prototype.map = function(id, source) {};

  DefaultLoader.prototype.normalize = function(moduleId, relativeTo) {
    return Promise.resolve(moduleId);
  };

  DefaultLoader.prototype.normalizeSync = function(moduleId, relativeTo) {
    return moduleId;
  };

  DefaultLoader.prototype.applyPluginToUrl = function(url, pluginName) {
    return `${pluginName}!${url}`;
  };

  DefaultLoader.prototype.addPlugin = function(pluginName, implementation) {
    let nonAnonDefine = define;
    nonAnonDefine(pluginName, [], {
      'load': function(name, req, onload) {
        let result = implementation.fetch(name);
        Promise.resolve(result).then(onload);
      }
    });
  };
} else {
  PLATFORM.eachModule = function(callback) {
    let modules = System._loader.modules;

    for (let key in modules) {
      try {
        if (callback(key, modules[key].module)) return;
      } catch (e) {}
    }
  };

  System.set('text', System.newModule({
    'translate': function(load) {
      return 'module.exports = "' + load.source
        .replace(/(["\\])/g, '\\$1')
        .replace(/[\f]/g, '\\f')
        .replace(/[\b]/g, '\\b')
        .replace(/[\n]/g, '\\n')
        .replace(/[\t]/g, '\\t')
        .replace(/[\r]/g, '\\r')
        .replace(/[\u2028]/g, '\\u2028')
        .replace(/[\u2029]/g, '\\u2029')
      + '";';
    }
  }));

  DefaultLoader.prototype._import = function(moduleId) {
    return System.import(moduleId);
  };

  DefaultLoader.prototype.loadModule = function(id) {
    return System.normalize(id).then(newId => {
      let existing = this.moduleRegistry[newId];
      if (existing !== undefined) {
        return Promise.resolve(existing);
      }

      return System.import(newId).then(m => {
        this.moduleRegistry[newId] = m;
        return ensureOriginOnExports(m, newId);
      });
    });
  };

  DefaultLoader.prototype.map = function(id, source) {
    System.map[id] = source;
  };

  DefaultLoader.prototype.normalizeSync = function(moduleId, relativeTo) {
    return System.normalizeSync(moduleId, relativeTo);
  };

  DefaultLoader.prototype.normalize = function(moduleId, relativeTo) {
    return System.normalize(moduleId, relativeTo);
  };

  DefaultLoader.prototype.applyPluginToUrl = function(url, pluginName) {
    return `${url}!${pluginName}`;
  };

  DefaultLoader.prototype.addPlugin = function(pluginName, implementation) {
    System.set(pluginName, System.newModule({
      'fetch': function(load, _fetch) {
        let result = implementation.fetch(load.address);
        return Promise.resolve(result).then(x => {
          load.metadata.result = x;
          return '';
        });
      },
      'instantiate': function(load) {
        return load.metadata.result;
      }
    }));
  };
}
<commit_msg>fix(DefaultLoader): use config API for map<commit_after>/*eslint dot-notation:0*/
import {Origin} from 'aurelia-metadata';
import {TemplateRegistryEntry, Loader} from 'aurelia-loader';
import {TextTemplateLoader} from './text-template-loader';
import {PLATFORM} from 'aurelia-pal';

function ensureOriginOnExports(executed, name) {
  let target = executed;
  let key;
  let exportedValue;

  if (target.__useDefault) {
    target = target['default'];
  }

  Origin.set(target, new Origin(name, 'default'));

  for (key in target) {
    exportedValue = target[key];

    if (typeof exportedValue === 'function') {
      Origin.set(exportedValue, new Origin(name, key));
    }
  }

  return executed;
}

/**
* Represents a template loader.
*/
interface TemplateLoader {
  /**
  * Loads a template.
  * @param loader The loader that is requesting the template load.
  * @param entry The TemplateRegistryEntry to load and populate with a template.
  * @return A promise which resolves when the TemplateRegistryEntry is loaded with a template.
  */
  loadTemplate(loader: Loader, entry: TemplateRegistryEntry): Promise<any>;
}

/**
* A default implementation of the Loader abstraction which works with SystemJS, RequireJS and Dojo Loader.
*/
export class DefaultLoader extends Loader {
  /**
  * The name of the underlying native loader plugin used to load text.
  */
  textPluginName: string = 'text';

  /**
  * Creates an instance of the DefaultLoader.
  */
  constructor() {
    super();

    this.moduleRegistry = Object.create(null);
    this.useTemplateLoader(new TextTemplateLoader());

    let that = this;

    this.addPlugin('template-registry-entry', {
      'fetch': function(address) {
        let entry = that.getOrCreateTemplateRegistryEntry(address);
        return entry.templateIsLoaded ? entry : that.templateLoader.loadTemplate(that, entry).then(x => entry);
      }
    });
  }

  /**
  * Instructs the loader to use a specific TemplateLoader instance for loading templates
  * @param templateLoader The instance of TemplateLoader to use for loading templates.
  */
  useTemplateLoader(templateLoader: TemplateLoader): void {
    this.templateLoader = templateLoader;
  }

  /**
  * Loads a collection of modules.
  * @param ids The set of module ids to load.
  * @return A Promise for an array of loaded modules.
  */
  loadAllModules(ids: string[]): Promise<any[]> {
    let loads = [];

    for (let i = 0, ii = ids.length; i < ii; ++i) {
      loads.push(this.loadModule(ids[i]));
    }

    return Promise.all(loads);
  }

  /**
  * Loads a template.
  * @param url The url of the template to load.
  * @return A Promise for a TemplateRegistryEntry containing the template.
  */
  loadTemplate(url: string): Promise<TemplateRegistryEntry> {
    return this._import(this.applyPluginToUrl(url, 'template-registry-entry'));
  }

  /**
  * Loads a text-based resource.
  * @param url The url of the text file to load.
  * @return A Promise for text content.
  */
  loadText(url: string): Promise<string> {
    return this._import(this.applyPluginToUrl(url, this.textPluginName)).then(textOrModule => {
      if (typeof textOrModule === 'string') {
        return textOrModule;
      }

      return textOrModule['default'];
    });
  }
}

PLATFORM.Loader = DefaultLoader;

if (!PLATFORM.global.System || !PLATFORM.global.System.import) {
  if (PLATFORM.global.requirejs && requirejs.s && requirejs.s.contexts && requirejs.s.contexts._ && requirejs.s.contexts._.defined) {
    PLATFORM.eachModule = function(callback) {
      let defined = requirejs.s.contexts._.defined;
      for (let key in defined) {
        try {
          if (callback(key, defined[key])) return;
        } catch (e) {}
      }
    };
  } else {
    PLATFORM.eachModule = function(callback) {};
  }

  DefaultLoader.prototype._import = function(moduleId) {
    return new Promise((resolve, reject) => {
      require([moduleId], resolve, reject);
    });
  };

  DefaultLoader.prototype.loadModule = function(id) {
    let existing = this.moduleRegistry[id];
    if (existing !== undefined) {
      return Promise.resolve(existing);
    }

    return new Promise((resolve, reject) => {
      require([id], m => {
        this.moduleRegistry[id] = m;
        resolve(ensureOriginOnExports(m, id));
      }, reject);
    });
  };

  DefaultLoader.prototype.map = function(id, source) {};

  DefaultLoader.prototype.normalize = function(moduleId, relativeTo) {
    return Promise.resolve(moduleId);
  };

  DefaultLoader.prototype.normalizeSync = function(moduleId, relativeTo) {
    return moduleId;
  };

  DefaultLoader.prototype.applyPluginToUrl = function(url, pluginName) {
    return `${pluginName}!${url}`;
  };

  DefaultLoader.prototype.addPlugin = function(pluginName, implementation) {
    let nonAnonDefine = define;
    nonAnonDefine(pluginName, [], {
      'load': function(name, req, onload) {
        let result = implementation.fetch(name);
        Promise.resolve(result).then(onload);
      }
    });
  };
} else {
  PLATFORM.eachModule = function(callback) {
    let modules = System._loader.modules;

    for (let key in modules) {
      try {
        if (callback(key, modules[key].module)) return;
      } catch (e) {}
    }
  };

  System.set('text', System.newModule({
    'translate': function(load) {
      return 'module.exports = "' + load.source
        .replace(/(["\\])/g, '\\$1')
        .replace(/[\f]/g, '\\f')
        .replace(/[\b]/g, '\\b')
        .replace(/[\n]/g, '\\n')
        .replace(/[\t]/g, '\\t')
        .replace(/[\r]/g, '\\r')
        .replace(/[\u2028]/g, '\\u2028')
        .replace(/[\u2029]/g, '\\u2029')
      + '";';
    }
  }));

  DefaultLoader.prototype._import = function(moduleId) {
    return System.import(moduleId);
  };

  DefaultLoader.prototype.loadModule = function(id) {
    return System.normalize(id).then(newId => {
      let existing = this.moduleRegistry[newId];
      if (existing !== undefined) {
        return Promise.resolve(existing);
      }

      return System.import(newId).then(m => {
        this.moduleRegistry[newId] = m;
        return ensureOriginOnExports(m, newId);
      });
    });
  };

  DefaultLoader.prototype.map = function(id, source) {
    System.config({ map: { [id]: source } });
  };

  DefaultLoader.prototype.normalizeSync = function(moduleId, relativeTo) {
    return System.normalizeSync(moduleId, relativeTo);
  };

  DefaultLoader.prototype.normalize = function(moduleId, relativeTo) {
    return System.normalize(moduleId, relativeTo);
  };

  DefaultLoader.prototype.applyPluginToUrl = function(url, pluginName) {
    return `${url}!${pluginName}`;
  };

  DefaultLoader.prototype.addPlugin = function(pluginName, implementation) {
    System.set(pluginName, System.newModule({
      'fetch': function(load, _fetch) {
        let result = implementation.fetch(load.address);
        return Promise.resolve(result).then(x => {
          load.metadata.result = x;
          return '';
        });
      },
      'instantiate': function(load) {
        return load.metadata.result;
      }
    }));
  };
}
<|endoftext|>


<commit_before>// 24 November 2014 / lhansen@mozilla.com

// A simple data-parallel framework that maintains a worker pool and
// invokes computations in parallel on shared memory.
//
// Load this into your main program, after loading barrier.js.
//
// Call Multicore.init() to set things up (see spec below).
//
// Call Multicore.build() to perform work (see spec below).

const Multicore =
    {
	init: _Multicore_init,
	build: _Multicore_build
    };

var _Multicore_workers = [];
var _Multicore_numWorkers = 0;
var _Multicore_callback = null;
var _Multicore_mem = null;
var _Multicore_alloc = 0;
var _Multicore_barrier = null;
var _Multicore_barrierLoc = null;
var _Multicore_funcLoc = 0;
var _Multicore_sizeLoc = 0;
var _Multicore_nextLoc = 0;
var _Multicore_limLoc = 0;
var _Multicore_nextArgLoc = 0;
var _Multicore_argLimLoc = 0;
var _Multicore_knownSAB = [];

// Multicore.init()
//
// numWorkers must be a positive integer.
// workerScript must be a URL.
// readyCallback must be a function, it will be called without arguments
//   when the workers have been set up.
//
// Call this function only once.

function _Multicore_init(numWorkers, workerScript, readyCallback) {
    _Multicore_numWorkers = numWorkers;
    _Multicore_mem = new SharedInt32Array(0x10000);
    _Multicore_alloc = 0;
    _Multicore_barrierLoc = _Multicore_alloc;
    _Multicore_barrier = new MasterBarrier(0x1337,
					   _Multicore_numWorkers,
					   _Multicore_mem,
					   _Multicore_barrierLoc,
					   barrierQuiescent);
    _Multicore_alloc += MasterBarrier.NUMLOCS;
    _Multicore_funcLoc = _Multicore_alloc++;
    _Multicore_sizeLoc = _Multicore_alloc++;
    _Multicore_nextLoc = _Multicore_alloc++;
    _Multicore_limLoc = _Multicore_alloc++;
    _Multicore_nextArgLoc = _Multicore_alloc++;
    _Multicore_argLimLoc = _Multicore_alloc++;
    _Multicore_callback = readyCallback;
    for ( var i=0 ; i < numWorkers ; i++ ) {
	var w = new Worker(workerScript);
	w.onmessage = messageHandler;
	w.postMessage(["start",
		       _Multicore_mem.buffer,
		       _Multicore_barrierLoc, _Multicore_funcLoc, _Multicore_sizeLoc, _Multicore_nextLoc, _Multicore_limLoc,
		       _Multicore_nextArgLoc, _Multicore_argLimLoc],
		      [_Multicore_mem.buffer]);
	_Multicore_workers.push(w);
    }

    function barrierQuiescent() {
	var fn;
	if (fn = _Multicore_callback) {
	    _Multicore_callback = null;
	    fn();
	}
	else
	    throw new Error("No barrier callback installed!");
    }

    function messageHandler(ev) {
	if (Array.isArray(ev.data) && ev.data.length >= 1) {
	    switch (ev.data[0]) {
	    case "MasterBarrier.dispatch":
		MasterBarrier.dispatch(ev.data[1]);
		break;
	    default:
		console.log(ev.data);
		break;
	    }
	}
	else
	    console.log(ev.data);
    }
}

// Multicore.build()
//
// doneCallback is a function, it will be invoked in the master once
//   the work is finished.
// fnIdent is the string identifier of the remote function to invoke.
//   The worker must register an appropriate handler.
// outputMem is a SharedTypedArray or SharedArrayBuffer that will (in
//   principle, though it's up to user code) receive the results of
//   the computation.
// indexSpace is an array of length-2 arrays determining the index
//   space of the computation; workers will be invoked on subvolumes
//   of this space in an unpredictable order.
// The ...args can be number, SharedTypedArray, or SharedArrayBuffer
//   values and will be marshalled and passed as arguments to the user
//   function on the worker side.
//
// You can call this function repeatedly, but only one call can be
// outstanding: only when the doneCallback has been invoked can
// Multicore.build be called again.

function _Multicore_build(doneCallback, fnIdent, outputMem, indexSpace, ...args) {
    const M = _Multicore_mem;

    const ARG_INT = 1;
    const ARG_FLOAT = 2;
    const ARG_SAB = 3;
    const ARG_ARRAY = 4;

    const TAG_SAB = 1;
    const TAG_I8 = 2;
    const TAG_U8 = 3;
    const TAG_CU8 = 4;
    const TAG_I16 = 5;
    const TAG_U16 = 6;
    const TAG_I32 = 7;
    const TAG_U32 = 8;
    const TAG_F32 = 9;
    const TAG_F64 = 10;
    const tmp = new ArrayBuffer(8);
    const itmp = new Int32Array(tmp);
    const ftmp = new Float64Array(tmp);

    for ( var x of indexSpace )
	if (x.length != 2 || typeof x[0] != 'number' || typeof x[1] != 'number' || (x[0]|0) != x[0] || (x[1]|0) != x[1])
	    throw new Error("Bad indexSpace element " + x)
    var items;
    switch (indexSpace.length) {
    case 1:
	items = sliceSpace(indexSpace[0][0], indexSpace[0][1]);
	break;
    case 2:
	items = cross(sliceSpace(indexSpace[0][0], indexSpace[0][1]), sliceSpace(indexSpace[1][0], indexSpace[1][1]));
	break;
    default:
	throw new Error("Only 1D and 2D supported as of now");
    }
    const itemSize = indexSpace.length * 2;
    var { argValues, newSAB } = processArgs(outputMem, args);
    if (newSAB.length) {
	_Multicore_callback =
	    function () {
		_Multicore_callback = doneCallback;
		var p = _Multicore_alloc;
		p = installArgs(p, argValues);
		p = installItems(p, fnIdent, itemSize, items);
		if (p >= M.length)
		    throw new Error("Not enough working memory");
		_Multicore_barrier.release();
	    };
	// Signal message loop exit.
	// Any negative number larger than numWorkers will do.
	M[_Multicore_funcLoc] = -1;
	M[_Multicore_sizeLoc] = 0;
	M[_Multicore_nextLoc] = -1000000;
	// Transmit buffers
	var xfer = [];
	for ( var x of newSAB )
	    xfer.push(x[0]);
	newSAB.unshift("transfer");
	for ( var w of _Multicore_workers )
	    w.postMessage(newSAB, xfer);
    }
    else {
	_Multicore_callback = doneCallback;
	var p = _Multicore_alloc;
	p = installArgs(p, argValues);
	p = installItems(p, fnIdent, itemSize, items);
	if (p >= M.length)
	    throw new Error("Not enough working memory");
    }
    _Multicore_barrier.release();

    function sliceSpace(lo, lim) {
	var items = [];
	var numItem = (lim - lo);
	var sliceHeight = Math.floor(numItem / (4*_Multicore_numWorkers));
	var extra = numItem % (4*_Multicore_numWorkers);
	while (lo < lim) {
	    var hi = lo + sliceHeight;
	    if (extra) {
		hi++;
		extra--;
	    }
	    items.push([lo, hi]);
	    lo = hi;
	}
	return items;
    }

    function cross(as, bs) {
	var items = [];
	for ( var a of as )
	    for ( var b of bs )
		items.push([a, b]);
	return items;
    }

    function installArgs(p, values) {
	M[_Multicore_nextArgLoc] = p;
	for ( var v of values )
	    M[p++] = v;
	M[_Multicore_argLimLoc] = p;
	return p;
    }

    function installItems(p, fn, wordsPerItem, items) {
	M[_Multicore_sizeLoc] = wordsPerItem;
	M[_Multicore_funcLoc] = p;
	M[p++] = fn.length;
	for ( var c of fn )
	    M[p++] = c.charCodeAt(0);
	M[_Multicore_nextLoc] = p;
	switch (wordsPerItem) {
	case 2:
	    for ( var i of items )
		for ( var j of i )
		    M[p++] = j;
	    break;
	case 4:
	    for ( var i of items )
		for ( var j of i )
		    for ( var k of j )
			M[p++] = k;
	    break;
	}
	M[_Multicore_limLoc] = p;
	return p;
    }

    function processArgs(outputMem, args) {
	var argValues = [];
	var newSAB = [];

	pushArg(outputMem, true);
	for ( var a of args )
	    pushArg(a);
	return { argValues, newSAB };

	function pushArg(v, isFirst) {
	    if (+v === v) {
		if (isFirst)
		    throw new Error("Output object must be a shared memory array");
		if ((v|0) === v) {
		    argValues.push(ARG_INT);
		    argValues.push(v);
		}
		else {
		    argValues.push(ARG_FLOAT);
		    if (argValues.length & 1)
			argValues.push(0);
		    ftmp[0] = v;
		    argValues.push(itmp[0]);
		    argValues.push(itmp[1]);
		}
		return;
	    }

	    if (v instanceof SharedArrayBuffer) {
		argValues.push(ARG_SAB);
		argValues.push(registerSab(v));
		return;
	    }

	    var tag = 0;
	    if (v instanceof SharedInt8Array)
		tag = TAG_I8;
	    else if (v instanceof SharedUint8Array)
		tag = TAG_U8;
	    else if (v instanceof SharedUint8ClampedArray)
		tag = TAG_CU8;
	    else if (v instanceof SharedInt16Array)
		tag = TAG_I16;
	    else if (v instanceof SharedUint16Array)
		tag = TAG_U16;
	    else if (v instanceof SharedInt32Array)
		tag = TAG_I32;
	    else if (v instanceof SharedUint32Array)
		tag = TAG_U32;
	    else if (v instanceof SharedFloat32Array)
		tag = TAG_F32;
	    else if (v instanceof SharedFloat64Array)
		tag = TAG_F64;
	    else
		throw new Error("Argument must be Number or shared array: " + v);

	    argValues.push(ARG_ARRAY | (tag << 8));
	    argValues.push(registerSab(v.buffer));
	    argValues.push(v.byteOffset);
	    argValues.push(v.length);
	}

	function registerSab(sab) {
	    for ( var i=0 ; i < _Multicore_knownSAB.length ; i++ )
		if (_Multicore_knownSAB[i] === x)
		    return i;
	    var k = _Multicore_knownSAB.length;
	    _Multicore_knownSAB.push(sab);
	    newSAB.push([sab, k]);
	    return k;
	}
    }
}

/*
Master/worker protocol.

There are seven distinguished locations in the private working memory
that are distributed to the workers on startup:

  barrierLoc  - the first location for the shared barrier sync
  funcLoc     - holds the function identifier for an invocation
  sizeLoc     - holds the number of words in a work item
  nextLoc     - holds the array index of the next work item
  limLoc      - holds the first array index past the last work item
  nextArgLoc  - holds the index of the first argument
  argLimLoc   - holds the first array index past the last argument

The worker creates a barrier on the barrierLoc and then enters that
barrier, and thus we're off.

The master has the following actions it wants to accomplish:

 - transfer new SAB values
 - perform parallel work

Transfer:

The workers are made to exit their message loop by passing a
distinguished value and releasing them from the barrier.  New messages
are then sent to them to transfer the new SAB values; the workers
receive them and register them and re-enter the message loop.

Computation:

One or more "arguments" are passed in the args area.

Suppose nextArgLoc < argLimLoc.  Let A=nextArgLoc and M be the
private working memory.

  the M[A] is a tag: int, float, sab, or array
  if tag==int, then the int follows immediately
  if tag==float, there may be padding and then the float follows immediately
    in native word order
  if tag==sab, then there is one argument word:
    - sab identifier
  if tag==array, then the tag identifies the array type, and there are
    the following three argument words:
    - sab identifier
    - byteoffset, or 0 for
    - element count

The first argument is always the output array, and it must be a SAB or
array type.

The arguments past the first are passed to the worker as arguments
after the index space arguments.

*/
<commit_msg>Clarification<commit_after>// 24 November 2014 / lhansen@mozilla.com

// A simple data-parallel framework that maintains a worker pool and
// invokes computations in parallel on shared memory.
//
// Load this into your main program, after loading barrier.js.
//
// Call Multicore.init() to set things up (see spec below).
//
// Call Multicore.build() to perform work (see spec below).

const Multicore =
    {
	init: _Multicore_init,
	build: _Multicore_build
    };

var _Multicore_workers = [];
var _Multicore_numWorkers = 0;
var _Multicore_callback = null;
var _Multicore_mem = null;
var _Multicore_alloc = 0;
var _Multicore_barrier = null;
var _Multicore_barrierLoc = null;
var _Multicore_funcLoc = 0;
var _Multicore_sizeLoc = 0;
var _Multicore_nextLoc = 0;
var _Multicore_limLoc = 0;
var _Multicore_nextArgLoc = 0;
var _Multicore_argLimLoc = 0;
var _Multicore_knownSAB = [];

// Multicore.init()
//
// numWorkers must be a positive integer.
// workerScript must be a URL.
// readyCallback must be a function, it will be called without arguments
//   when the workers have been set up.
//
// Call this function only once.

function _Multicore_init(numWorkers, workerScript, readyCallback) {
    _Multicore_numWorkers = numWorkers;
    _Multicore_mem = new SharedInt32Array(0x10000);
    _Multicore_alloc = 0;
    _Multicore_barrierLoc = _Multicore_alloc;
    _Multicore_barrier = new MasterBarrier(0x1337,
					   _Multicore_numWorkers,
					   _Multicore_mem,
					   _Multicore_barrierLoc,
					   barrierQuiescent);
    _Multicore_alloc += MasterBarrier.NUMLOCS;
    _Multicore_funcLoc = _Multicore_alloc++;
    _Multicore_sizeLoc = _Multicore_alloc++;
    _Multicore_nextLoc = _Multicore_alloc++;
    _Multicore_limLoc = _Multicore_alloc++;
    _Multicore_nextArgLoc = _Multicore_alloc++;
    _Multicore_argLimLoc = _Multicore_alloc++;
    _Multicore_callback = readyCallback;
    for ( var i=0 ; i < numWorkers ; i++ ) {
	var w = new Worker(workerScript);
	w.onmessage = messageHandler;
	w.postMessage(["start",
		       _Multicore_mem.buffer,
		       _Multicore_barrierLoc, _Multicore_funcLoc, _Multicore_sizeLoc, _Multicore_nextLoc, _Multicore_limLoc,
		       _Multicore_nextArgLoc, _Multicore_argLimLoc],
		      [_Multicore_mem.buffer]);
	_Multicore_workers.push(w);
    }

    function barrierQuiescent() {
	var fn;
	if (fn = _Multicore_callback) {
	    _Multicore_callback = null;
	    fn();
	}
	else
	    throw new Error("No barrier callback installed!");
    }

    function messageHandler(ev) {
	if (Array.isArray(ev.data) && ev.data.length >= 1) {
	    switch (ev.data[0]) {
	    case "MasterBarrier.dispatch":
		MasterBarrier.dispatch(ev.data[1]);
		break;
	    default:
		console.log(ev.data);
		break;
	    }
	}
	else
	    console.log(ev.data);
    }
}

// Multicore.build()
//
// doneCallback is a function, it will be invoked in the master once
//   the work is finished.
// fnIdent is the string identifier of the remote function to invoke.
//   The worker must register an appropriate handler.
// outputMem is a SharedTypedArray or SharedArrayBuffer that will (in
//   principle, though it's up to user code) receive the results of
//   the computation.
// indexSpace is an array of length-2 arrays determining the index
//   space of the computation; workers will be invoked on subvolumes
//   of this space in an unpredictable order.
// The ...args can be number, SharedTypedArray, or SharedArrayBuffer
//   values and will be marshalled and passed as arguments to the user
//   function on the worker side.
//
// You can call this function repeatedly, but only one call can be
// outstanding: only when the doneCallback has been invoked can
// Multicore.build be called again.

function _Multicore_build(doneCallback, fnIdent, outputMem, indexSpace, ...args) {
    const M = _Multicore_mem;

    const ARG_INT = 1;
    const ARG_FLOAT = 2;
    const ARG_SAB = 3;
    const ARG_ARRAY = 4;

    const TAG_SAB = 1;
    const TAG_I8 = 2;
    const TAG_U8 = 3;
    const TAG_CU8 = 4;
    const TAG_I16 = 5;
    const TAG_U16 = 6;
    const TAG_I32 = 7;
    const TAG_U32 = 8;
    const TAG_F32 = 9;
    const TAG_F64 = 10;
    const tmp = new ArrayBuffer(8);
    const itmp = new Int32Array(tmp);
    const ftmp = new Float64Array(tmp);

    for ( var x of indexSpace )
	if (x.length != 2 || typeof x[0] != 'number' || typeof x[1] != 'number' || (x[0]|0) != x[0] || (x[1]|0) != x[1])
	    throw new Error("Bad indexSpace element " + x)
    var items;
    switch (indexSpace.length) {
    case 1:
	items = sliceSpace(indexSpace[0][0], indexSpace[0][1]);
	break;
    case 2:
	items = cross(sliceSpace(indexSpace[0][0], indexSpace[0][1]), sliceSpace(indexSpace[1][0], indexSpace[1][1]));
	break;
    default:
	throw new Error("Only 1D and 2D supported as of now");
    }
    const itemSize = indexSpace.length * 2;
    var { argValues, newSAB } = processArgs(outputMem, args);
    if (newSAB.length) {
	_Multicore_callback =
	    function () {
		_Multicore_callback = doneCallback;
		var p = _Multicore_alloc;
		p = installArgs(p, argValues);
		p = installItems(p, fnIdent, itemSize, items);
		if (p >= M.length)
		    throw new Error("Not enough working memory");
		_Multicore_barrier.release();
	    };
	// Signal message loop exit.
	// Any negative number larger than numWorkers will do.
	M[_Multicore_funcLoc] = -1;
	M[_Multicore_sizeLoc] = 0;
	M[_Multicore_nextLoc] = -1000000;
	// Transmit buffers
	var xfer = [];
	for ( var x of newSAB )
	    xfer.push(x[0]);
	newSAB.unshift("transfer");
	for ( var w of _Multicore_workers )
	    w.postMessage(newSAB, xfer);
    }
    else {
	_Multicore_callback = doneCallback;
	var p = _Multicore_alloc;
	p = installArgs(p, argValues);
	p = installItems(p, fnIdent, itemSize, items);
	if (p >= M.length)
	    throw new Error("Not enough working memory");
    }
    _Multicore_barrier.release();

    function sliceSpace(lo, lim) {
	var items = [];
	var numItem = (lim - lo);
	var sliceHeight = Math.floor(numItem / (4*_Multicore_numWorkers));
	var extra = numItem % (4*_Multicore_numWorkers);
	while (lo < lim) {
	    var hi = lo + sliceHeight;
	    if (extra) {
		hi++;
		extra--;
	    }
	    items.push([lo, hi]);
	    lo = hi;
	}
	return items;
    }

    function cross(as, bs) {
	var items = [];
	for ( var a of as )
	    for ( var b of bs )
		items.push([a, b]);
	return items;
    }

    function installArgs(p, values) {
	M[_Multicore_nextArgLoc] = p;
	for ( var v of values )
	    M[p++] = v;
	M[_Multicore_argLimLoc] = p;
	return p;
    }

    function installItems(p, fn, wordsPerItem, items) {
	M[_Multicore_sizeLoc] = wordsPerItem;
	M[_Multicore_funcLoc] = p;
	M[p++] = fn.length;
	for ( var c of fn )
	    M[p++] = c.charCodeAt(0);
	M[_Multicore_nextLoc] = p;
	switch (wordsPerItem) {
	case 2:
	    for ( var i of items )
		for ( var j of i )
		    M[p++] = j;
	    break;
	case 4:
	    for ( var i of items )
		for ( var j of i )
		    for ( var k of j )
			M[p++] = k;
	    break;
	}
	M[_Multicore_limLoc] = p;
	return p;
    }

    function processArgs(outputMem, args) {
	var argValues = [];
	var newSAB = [];

	pushArg(outputMem, true);
	for ( var a of args )
	    pushArg(a);
	return { argValues, newSAB };

	function pushArg(v, isFirst) {
	    if (+v === v) {
		if (isFirst)
		    throw new Error("Output object must be a shared memory array");
		if ((v|0) === v) {
		    argValues.push(ARG_INT);
		    argValues.push(v);
		}
		else {
		    argValues.push(ARG_FLOAT);
		    if (argValues.length & 1)
			argValues.push(0);
		    ftmp[0] = v;
		    argValues.push(itmp[0]);
		    argValues.push(itmp[1]);
		}
		return;
	    }

	    if (v instanceof SharedArrayBuffer) {
		argValues.push(ARG_SAB);
		argValues.push(registerSab(v));
		return;
	    }

	    var tag = 0;
	    if (v instanceof SharedInt8Array)
		tag = TAG_I8;
	    else if (v instanceof SharedUint8Array)
		tag = TAG_U8;
	    else if (v instanceof SharedUint8ClampedArray)
		tag = TAG_CU8;
	    else if (v instanceof SharedInt16Array)
		tag = TAG_I16;
	    else if (v instanceof SharedUint16Array)
		tag = TAG_U16;
	    else if (v instanceof SharedInt32Array)
		tag = TAG_I32;
	    else if (v instanceof SharedUint32Array)
		tag = TAG_U32;
	    else if (v instanceof SharedFloat32Array)
		tag = TAG_F32;
	    else if (v instanceof SharedFloat64Array)
		tag = TAG_F64;
	    else
		throw new Error("Argument must be Number or shared array: " + v);

	    argValues.push(ARG_ARRAY | (tag << 8));
	    argValues.push(registerSab(v.buffer));
	    argValues.push(v.byteOffset);
	    argValues.push(v.length);
	}

	function registerSab(sab) {
	    for ( var i=0 ; i < _Multicore_knownSAB.length ; i++ )
		if (_Multicore_knownSAB[i] === x)
		    return i;
	    var k = _Multicore_knownSAB.length;
	    _Multicore_knownSAB.push(sab);
	    newSAB.push([sab, k]);
	    return k;
	}
    }
}

/*
Master/worker protocol.

There are seven distinguished locations in the private working memory
that are distributed to the workers on startup:

  barrierLoc  - the first location for the shared barrier sync
  funcLoc     - holds the index of the function name representation
  sizeLoc     - holds the number of words in a work item
  nextLoc     - holds the array index of the next work item
  limLoc      - holds the first array index past the last work item
  nextArgLoc  - holds the index of the first argument
  argLimLoc   - holds the first array index past the last argument

The function name is a sequence of values: the first value is the
number of characters in the name, then follows one character per
element.

The worker creates a barrier on the barrierLoc and then enters that
barrier, and thus we're off.

The master has the following actions it wants to accomplish:

 - transfer new SAB values
 - perform parallel work

Transfer:

The workers are made to exit their message loop by passing a
distinguished value and releasing them from the barrier.  New messages
are then sent to them to transfer the new SAB values; the workers
receive them and register them and re-enter the message loop.

Computation:

One or more "arguments" are passed in the args area.

Suppose nextArgLoc < argLimLoc.  Let A=nextArgLoc and M be the
private working memory.

  the M[A] is a tag: int, float, sab, or array
  if tag==int, then the int follows immediately
  if tag==float, there may be padding and then the float follows immediately
    in native word order
  if tag==sab, then there is one argument word:
    - sab identifier
  if tag==array, then the tag identifies the array type, and there are
    the following three argument words:
    - sab identifier
    - byteoffset, or 0 for
    - element count

The first argument is always the output array, and it must be a SAB or
array type.

The arguments past the first are passed to the worker as arguments
after the index space arguments.

*/
<|endoftext|>


<commit_before>import {Events, Playback, Styler, template} from 'Clappr'

import playbackStyle from './public/style.css'
import playbackHtml from './public/youtube.html'

class YoutubePlayback extends Playback {
  get name() { return 'youtube_playback' }

  get template() { return template(playbackHtml) }

  get attributes() {
    return {
      'data-youtube-playback': '',
      class: 'clappr-youtube-playback',
      id: this.cid
    }
  }

  get ended() { return false }
  get buffering() { return this.player && this.player.getPlayerState() === YT.PlayerState.BUFFERING }
  get isReady() { return this._ready }

  constructor(options) {
    super(options)
    this.options = options
    this.settings = {
      changeCount: 0,
      seekEnabled: true,
      left: ['playpause', 'position', 'duration'],
      default: ['seekbar'],
      right:['fullscreen','volume', 'hd-indicator']
    }
    Clappr.Mediator.on(Clappr.Events.PLAYER_RESIZE, this.updateSize, this)
    this.embedYoutubeApiScript()
  }

  setupYoutubePlayer() {
    if (window.YT && window.YT.Player) {
      this.embedYoutubePlayer()
    } else {
      this.once(Clappr.Events.PLAYBACK_READY, () => this.embedYoutubePlayer())
    }
  }

  embedYoutubeApiScript() {
      let script = document.createElement('script')
      script.setAttribute('type', 'text/javascript')
      script.setAttribute('async', 'async')
      script.setAttribute('src', 'https://www.youtube.com/iframe_api')
      document.body.appendChild(script)
      window.onYouTubeIframeAPIReady = () => this.ready()
  }

  embedYoutubePlayer() {
    let playerVars = {
      controls: 0,
      autoplay: 1,
      disablekb: 1,
      enablejsapi: 1,
      iv_load_policy: 3,
      modestbranding: 1,
      showinfo: 0,
      html5: 1
    }
    if (this.options.youtubePlaylist) {
      playerVars.listType = 'playlist'
      playerVars.list = this.options.youtubePlaylist
    }
    this.player = new YT.Player(`yt${this.cid}`, {
      videoId: this.options.src,
      playerVars: playerVars,
      events: {
        onReady: () => this.ready(),
        onStateChange: (event) => this.stateChange(event),
        onPlaybackQualityChange: (event) => this.qualityChange(event)
      }
    })
  }

  updateSize() {
    this.player && this.player.setSize(this.$el.width(), this.$el.height())
  }

  ready() {
    this._ready = true
    this.trigger(Events.PLAYBACK_READY)
  }

  qualityChange(event) {
    this.trigger(Events.PLAYBACK_HIGHDEFINITIONUPDATE, this.isHighDefinitionInUse())
  }

  stateChange(event) {
    switch (event.data) {
      case YT.PlayerState.PLAYING:
        this.enableMediaControl()
        let playbackType = this.getPlaybackType()
        if (this._playbackType !== playbackType) {
          this.settings.changeCount++
          this._playbackType = playbackType
          this.trigger(Events.PLAYBACK_SETTINGSUPDATE)
        }
        this.trigger(Events.PLAYBACK_BUFFERFULL)
        this.trigger(Events.PLAYBACK_PLAY)
        break
      case YT.PlayerState.PAUSED:
        this.trigger(Events.PLAYBACK_PAUSE)
        break
      case YT.PlayerState.BUFFERING:
        this.trigger(Events.PLAYBACK_BUFFERING)
        break
      case YT.PlayerState.ENDED:
        if (this.options.youtubeShowRelated) {
          this.disableMediaControl()
        } else {
          this.trigger(Events.PLAYBACK_ENDED)
        }
        break
      default: break
    }
  }

  play() {
    if (this.player) {
      this._progressTimer = this._progressTimer || setInterval(() => this.progress(), 100)
      this._timeupdateTimer = this._timeupdateTimer || setInterval(() => this.timeupdate(), 100)
      this.player.playVideo()
    } else if (this._ready) {
      this.trigger(Events.PLAYBACK_BUFFERING)
      this._progressTimer = this._progressTimer || setInterval(() => this.progress(), 100)
      this._timeupdateTimer = this._timeupdateTimer || setInterval(() => this.timeupdate(), 100)
      this.setupYoutubePlayer()
    } else {
      this.trigger(Events.PLAYBACK_BUFFERING)
      this.listenToOnce(this, Clappr.Events.PLAYBACK_READY, this.play)
    }
  }

  pause() {
    clearInterval(this._timeupdateTimer)
    this._timeupdateTimer = null
    this.player && this.player.pauseVideo()
  }

  seek(time) {
    if (!this.player) return
    this.player.seekTo(time)
  }

  seekPercentage(percentage) {
    if (!this.player) return
    let duration = this.player.getDuration()
    let time = percentage * duration / 100
    this.seekTo(time)
  }

  volume(value) {
    this.player && this.player.setVolume && this.player.setVolume(value)
  }

  progress() {
    if (!this.player || !this.player.getDuration) return
    let buffered = this.player.getDuration() * this.player.getVideoLoadedFraction()
    this.trigger(Clappr.Events.PLAYBACK_PROGRESS, {start: 0, current: buffered, total: this.player.getDuration()})
  }

  timeupdate() {
    if (!this.player || !this.player.getDuration) return
    this.trigger(Clappr.Events.PLAYBACK_TIMEUPDATE, {current: this.player.getCurrentTime(), total: this.player.getDuration()})
  }

  isPlaying() {
    return this.player && this.player.getPlayerState() == YT.PlayerState.PLAYING
  }

  isHighDefinitionInUse() {
    return this.player && !!this.player.getPlaybackQuality().match(/^hd\d+/)
  }

  getDuration() {
    let duration = 0
    if (this.player) {
      duration = this.player.getDuration()
    }
    return duration
  }

  getPlaybackType() {
    return Playback.VOD
  }

  disableMediaControl() {
    this.$el.css({'pointer-events': 'auto'})
    this.trigger(Clappr.Events.PLAYBACK_MEDIACONTROL_DISABLE)
  }

  enableMediaControl() {
    this.$el.css({'pointer-events': 'none'})
    this.trigger(Clappr.Events.PLAYBACK_MEDIACONTROL_ENABLE)
  }

  render() {
    this.$el.html(this.template({id: `yt${this.cid}`}))
    let style = Styler.getStyleFor(playbackStyle, {baseUrl: this.options.baseUrl})
    this.$el.append(style)
    return this;
  }
}

YoutubePlayback.canPlay = function(source) {
  return true;
};

module.exports = window.YoutubePlayback = YoutubePlayback;
<commit_msg>plugin: fix exception when setting options<commit_after>import {Events, Playback, Styler, template} from 'Clappr'

import playbackStyle from './public/style.css'
import playbackHtml from './public/youtube.html'

class YoutubePlayback extends Playback {
  get name() { return 'youtube_playback' }

  get template() { return template(playbackHtml) }

  get attributes() {
    return {
      'data-youtube-playback': '',
      class: 'clappr-youtube-playback',
      id: this.cid
    }
  }

  get ended() { return false }
  get buffering() { return this.player && this.player.getPlayerState() === YT.PlayerState.BUFFERING }
  get isReady() { return this._ready }

  constructor(options) {
    super(options)
    this.settings = {
      changeCount: 0,
      seekEnabled: true,
      left: ['playpause', 'position', 'duration'],
      default: ['seekbar'],
      right:['fullscreen','volume', 'hd-indicator']
    }
    Clappr.Mediator.on(Clappr.Events.PLAYER_RESIZE, this.updateSize, this)
    this.embedYoutubeApiScript()
  }

  setupYoutubePlayer() {
    if (window.YT && window.YT.Player) {
      this.embedYoutubePlayer()
    } else {
      this.once(Clappr.Events.PLAYBACK_READY, () => this.embedYoutubePlayer())
    }
  }

  embedYoutubeApiScript() {
      let script = document.createElement('script')
      script.setAttribute('type', 'text/javascript')
      script.setAttribute('async', 'async')
      script.setAttribute('src', 'https://www.youtube.com/iframe_api')
      document.body.appendChild(script)
      window.onYouTubeIframeAPIReady = () => this.ready()
  }

  embedYoutubePlayer() {
    let playerVars = {
      controls: 0,
      autoplay: 1,
      disablekb: 1,
      enablejsapi: 1,
      iv_load_policy: 3,
      modestbranding: 1,
      showinfo: 0,
      html5: 1
    }
    if (this.options.youtubePlaylist) {
      playerVars.listType = 'playlist'
      playerVars.list = this.options.youtubePlaylist
    }
    this.player = new YT.Player(`yt${this.cid}`, {
      videoId: this.options.src,
      playerVars: playerVars,
      events: {
        onReady: () => this.ready(),
        onStateChange: (event) => this.stateChange(event),
        onPlaybackQualityChange: (event) => this.qualityChange(event)
      }
    })
  }

  updateSize() {
    this.player && this.player.setSize(this.$el.width(), this.$el.height())
  }

  ready() {
    this._ready = true
    this.trigger(Events.PLAYBACK_READY)
  }

  qualityChange(event) {
    this.trigger(Events.PLAYBACK_HIGHDEFINITIONUPDATE, this.isHighDefinitionInUse())
  }

  stateChange(event) {
    switch (event.data) {
      case YT.PlayerState.PLAYING:
        this.enableMediaControl()
        let playbackType = this.getPlaybackType()
        if (this._playbackType !== playbackType) {
          this.settings.changeCount++
          this._playbackType = playbackType
          this.trigger(Events.PLAYBACK_SETTINGSUPDATE)
        }
        this.trigger(Events.PLAYBACK_BUFFERFULL)
        this.trigger(Events.PLAYBACK_PLAY)
        break
      case YT.PlayerState.PAUSED:
        this.trigger(Events.PLAYBACK_PAUSE)
        break
      case YT.PlayerState.BUFFERING:
        this.trigger(Events.PLAYBACK_BUFFERING)
        break
      case YT.PlayerState.ENDED:
        if (this.options.youtubeShowRelated) {
          this.disableMediaControl()
        } else {
          this.trigger(Events.PLAYBACK_ENDED)
        }
        break
      default: break
    }
  }

  play() {
    if (this.player) {
      this._progressTimer = this._progressTimer || setInterval(() => this.progress(), 100)
      this._timeupdateTimer = this._timeupdateTimer || setInterval(() => this.timeupdate(), 100)
      this.player.playVideo()
    } else if (this._ready) {
      this.trigger(Events.PLAYBACK_BUFFERING)
      this._progressTimer = this._progressTimer || setInterval(() => this.progress(), 100)
      this._timeupdateTimer = this._timeupdateTimer || setInterval(() => this.timeupdate(), 100)
      this.setupYoutubePlayer()
    } else {
      this.trigger(Events.PLAYBACK_BUFFERING)
      this.listenToOnce(this, Clappr.Events.PLAYBACK_READY, this.play)
    }
  }

  pause() {
    clearInterval(this._timeupdateTimer)
    this._timeupdateTimer = null
    this.player && this.player.pauseVideo()
  }

  seek(time) {
    if (!this.player) return
    this.player.seekTo(time)
  }

  seekPercentage(percentage) {
    if (!this.player) return
    let duration = this.player.getDuration()
    let time = percentage * duration / 100
    this.seekTo(time)
  }

  volume(value) {
    this.player && this.player.setVolume && this.player.setVolume(value)
  }

  progress() {
    if (!this.player || !this.player.getDuration) return
    let buffered = this.player.getDuration() * this.player.getVideoLoadedFraction()
    this.trigger(Clappr.Events.PLAYBACK_PROGRESS, {start: 0, current: buffered, total: this.player.getDuration()})
  }

  timeupdate() {
    if (!this.player || !this.player.getDuration) return
    this.trigger(Clappr.Events.PLAYBACK_TIMEUPDATE, {current: this.player.getCurrentTime(), total: this.player.getDuration()})
  }

  isPlaying() {
    return this.player && this.player.getPlayerState() == YT.PlayerState.PLAYING
  }

  isHighDefinitionInUse() {
    return this.player && !!this.player.getPlaybackQuality().match(/^hd\d+/)
  }

  getDuration() {
    let duration = 0
    if (this.player) {
      duration = this.player.getDuration()
    }
    return duration
  }

  getPlaybackType() {
    return Playback.VOD
  }

  disableMediaControl() {
    this.$el.css({'pointer-events': 'auto'})
    this.trigger(Clappr.Events.PLAYBACK_MEDIACONTROL_DISABLE)
  }

  enableMediaControl() {
    this.$el.css({'pointer-events': 'none'})
    this.trigger(Clappr.Events.PLAYBACK_MEDIACONTROL_ENABLE)
  }

  render() {
    this.$el.html(this.template({id: `yt${this.cid}`}))
    let style = Styler.getStyleFor(playbackStyle, {baseUrl: this.options.baseUrl})
    this.$el.append(style)
    return this;
  }
}

YoutubePlayback.canPlay = function(source) {
  return true;
};

module.exports = window.YoutubePlayback = YoutubePlayback;
<|endoftext|>


<commit_before>(function(context, MM) {
    var easey = {},
        i; // the interval!

    function easeIn(t) { return t * t; }
    function easeOut(t) { return Math.sin(t * Math.PI / 2); }

    easey.slow = function(map, goal, duration) {
        if (i) { window.clearInterval(i); }

        var start = (+new Date()),
            startZoom = map.getZoom(),
            startCenter = map.getCenter();

        if (typeof goal === 'number') {
            goal = { zoom: goal };
        } else if (goal.lat && typeof goal.lat === 'number') {
            goal = { pan: goal };
        }

        z = goal.zoom || startZoom;
        to = goal.pan || startCenter;

        i = window.setInterval(function() {
            // use shift-double-click to zoom out
            var delta = (+new Date()) - start;
            if (delta > duration) {
                map.setZoom(z);
                return window.clearInterval(i);
            }

            var t = easeOut(delta / duration);
            map.setCenterZoom(MM.Location.interpolate(startCenter, to, t),
                z == startZoom ? z : (startZoom * (1 - t) + z * t));
            map.draw();
        }, 0);
    };

    // Handle double clicks, that zoom the map in one zoom level.
    easey.DoubleClickHandler = function(map) {
        if (map !== undefined) {
            this.init(map);
        }
    };

    easey.DoubleClickHandler.prototype = {

        init: function(map) {
            this.map = map;
            MM.addEvent(map.parent, 'dblclick', this.getDoubleClick());
        },

        doubleClickHandler: null,

        getDoubleClick: function() {

            // Ensure that this handler is attached once.
            if (!this.doubleClickHandler) {
                var theHandler = this;
                this.doubleClickHandler = function(e) {
                    var map = theHandler.map,
                        point = map.pointLocation(MM.getMousePoint(e, map)),
                        z = map.getZoom() + (e.shiftKey ? -1 : 1);

                    easey.slow(map, {
                        zoom: z,
                        pan: point
                    }, 200);

                    return MM.cancelEvent(e);
                };
            }
            return this.doubleClickHandler;
        }
    };

    // A handler that allows mouse-wheel zooming - zooming in
    // when page would scroll up, and out when the page would scroll down.
    easey.MouseWheelHandler = function(map) {
        if (map !== undefined) {
            this.init(map);
        }
    };

    easey.MouseWheelHandler.prototype = {

        init: function(map) {
            this.map = map;
            MM.addEvent(map.parent, 'mousewheel', this.getMouseWheel());
        },

        mouseWheelHandler: null,

        getMouseWheel: function() {
            // Ensure that this handler is attached once.
            if (!this.mouseWheelHandler) {
                var theHandler = this;
                var prevTime = new Date().getTime();
                this.mouseWheelHandler = function(e) {

                    var delta = 0;
                    if (e.wheelDelta) {
                        delta = e.wheelDelta;
                    } else if (e.detail) {
                        delta = -e.detail;
                    }

                    // limit mousewheeling to once every 200ms
                    var timeSince = new Date().getTime() - prevTime;

                    if (Math.abs(delta) > 0 && (timeSince > 200)) {
                        var map = theHandler.map,
                            point = map.pointLocation(MM.getMousePoint(e, map)),
                            z = map.getZoom();
                        easey.slow(map, {
                            zoom: z + (delta > 0 ? 1 : -1),
                            pan: point
                        }, 100);

                        prevTime = new Date().getTime();
                    }

                    // Cancel the event so that the page doesn't scroll
                    return MM.cancelEvent(e);
                };
            }
            return this.mouseWheelHandler;
        }
    };

    // Handle the use of mouse dragging to pan the map.
    easey.DragHandler = function(map) {
        if (map !== undefined) {
            this.init(map);
        }
    };

    easey.DragHandler.prototype = {

        init: function(map) {
            this.map = map;
            MM.addEvent(map.parent, 'mousedown', MM.bind(this.mouseDown, this));
        },

        mouseDown: function(e) {
            MM.addEvent(document, 'mouseup', this._mouseUp = MM.bind(this.mouseUp, this));
            MM.addEvent(document, 'mousemove', this._mouseMove = MM.bind(this.mouseMove, this));

            this.lastMouse = MM.getMousePoint(e, this.map)
            this.prevMouse = MM.getMousePoint(e, this.map)
            this.map.parent.style.cursor = 'move';

            return MM.cancelEvent(e);
        },

        mouseMove: function(e) {
            if (this.prevMouse) {
                var nextMouse = MM.getMousePoint(e, this.map);
                this.map.panBy(
                    nextMouse.x - this.prevMouse.x,
                    nextMouse.y - this.prevMouse.y);

                this.lastMouse = new MM.Point(this.prevMouse.x, this.prevMouse.y);
                this.prevMouse = MM.getMousePoint(e, this.map);

                this.prevMouse.t = +new Date();
            }

            return MM.cancelEvent(e);
        },

        mouseUp: function(e) {
            MM.removeEvent(document, 'mouseup', this._mouseUp);
            MM.removeEvent(document, 'mousemove', this._mouseMove);

            var angle = Math.atan2(
                this.lastMouse.y - this.prevMouse.y,
                this.lastMouse.x - this.prevMouse.x);
            var distance = MM.Point.distance(this.lastMouse, this.prevMouse);
            var speed = Math.min(Math.log(1 + (distance / ((+new Date()) - this.prevMouse.t))) * 90, 300);

            if (isNaN(angle)) return;

            var center = this.map.coordinatePoint(this.map.coordinate);
            var pan = this.map.pointLocation(new MM.Point(
                center.x + (Math.cos(angle) * speed),
                center.y + (Math.sin(angle) * speed)));

            easey.slow(this.map, {
                pan: pan
            }, 200);
            this.prevMouse = null;
            this.map.parent.style.cursor = '';

            return MM.cancelEvent(e);
        }
    };

    this.easey = easey;
})(this, com.modestmaps);
<commit_msg>Constant speed over distance<commit_after>(function(context, MM) {
    var easey = {},
        i; // the interval!

    function easeIn(t) { return t * t; }
    function easeOut(t) { return Math.sin(t * Math.PI / 2); }

    easey.slow = function(map, goal, duration) {
        if (i) { window.clearInterval(i); }

        var start = (+new Date()),
            startZoom = map.getZoom(),
            startCenter = map.getCenter();

        if (typeof goal === 'number') {
            goal = { zoom: goal };
        } else if (goal.lat && typeof goal.lat === 'number') {
            goal = { pan: goal };
        }

        z = goal.zoom || startZoom;
        to = goal.pan || startCenter;

        i = window.setInterval(function() {
            // use shift-double-click to zoom out
            var delta = (+new Date()) - start;
            if (delta > duration) {
                map.setZoom(z);
                return window.clearInterval(i);
            }

            var t = easeOut(delta / duration);
            map.setCenterZoom(MM.Location.interpolate(startCenter, to, t),
                z == startZoom ? z : (startZoom * (1 - t) + z * t));
            map.draw();
        }, 0);
    };

    // Handle double clicks, that zoom the map in one zoom level.
    easey.DoubleClickHandler = function(map) {
        if (map !== undefined) {
            this.init(map);
        }
    };

    easey.DoubleClickHandler.prototype = {

        init: function(map) {
            this.map = map;
            MM.addEvent(map.parent, 'dblclick', this.getDoubleClick());
        },

        doubleClickHandler: null,

        getDoubleClick: function() {

            // Ensure that this handler is attached once.
            if (!this.doubleClickHandler) {
                var theHandler = this;
                this.doubleClickHandler = function(e) {
                    var map = theHandler.map,
                        point = map.pointLocation(MM.getMousePoint(e, map)),
                        z = map.getZoom() + (e.shiftKey ? -1 : 1);

                    easey.slow(map, {
                        zoom: z,
                        pan: point
                    }, 200);

                    return MM.cancelEvent(e);
                };
            }
            return this.doubleClickHandler;
        }
    };

    // A handler that allows mouse-wheel zooming - zooming in
    // when page would scroll up, and out when the page would scroll down.
    easey.MouseWheelHandler = function(map) {
        if (map !== undefined) {
            this.init(map);
        }
    };

    easey.MouseWheelHandler.prototype = {

        init: function(map) {
            this.map = map;
            MM.addEvent(map.parent, 'mousewheel', this.getMouseWheel());
        },

        mouseWheelHandler: null,

        getMouseWheel: function() {
            // Ensure that this handler is attached once.
            if (!this.mouseWheelHandler) {
                var theHandler = this;
                var prevTime = new Date().getTime();
                this.mouseWheelHandler = function(e) {

                    var delta = 0;
                    if (e.wheelDelta) {
                        delta = e.wheelDelta;
                    } else if (e.detail) {
                        delta = -e.detail;
                    }

                    // limit mousewheeling to once every 200ms
                    var timeSince = new Date().getTime() - prevTime;

                    if (Math.abs(delta) > 0 && (timeSince > 200)) {
                        var map = theHandler.map,
                            point = map.pointLocation(MM.getMousePoint(e, map)),
                            z = map.getZoom();
                        easey.slow(map, {
                            zoom: z + (delta > 0 ? 1 : -1),
                            pan: point
                        }, 100);

                        prevTime = new Date().getTime();
                    }

                    // Cancel the event so that the page doesn't scroll
                    return MM.cancelEvent(e);
                };
            }
            return this.mouseWheelHandler;
        }
    };

    // Handle the use of mouse dragging to pan the map.
    easey.DragHandler = function(map) {
        if (map !== undefined) {
            this.init(map);
        }
    };

    easey.DragHandler.prototype = {

        init: function(map) {
            this.map = map;
            MM.addEvent(map.parent, 'mousedown', MM.bind(this.mouseDown, this));
        },

        mouseDown: function(e) {
            MM.addEvent(document, 'mouseup', this._mouseUp = MM.bind(this.mouseUp, this));
            MM.addEvent(document, 'mousemove', this._mouseMove = MM.bind(this.mouseMove, this));

            this.lastMouse = MM.getMousePoint(e, this.map)
            this.prevMouse = MM.getMousePoint(e, this.map)
            this.map.parent.style.cursor = 'move';

            return MM.cancelEvent(e);
        },

        mouseMove: function(e) {
            if (this.prevMouse) {
                var nextMouse = MM.getMousePoint(e, this.map);
                this.map.panBy(
                    nextMouse.x - this.prevMouse.x,
                    nextMouse.y - this.prevMouse.y);

                this.lastMouse = new MM.Point(this.prevMouse.x, this.prevMouse.y);
                this.prevMouse = MM.getMousePoint(e, this.map);

                this.prevMouse.t = +new Date();
            }

            return MM.cancelEvent(e);
        },

        mouseUp: function(e) {
            MM.removeEvent(document, 'mouseup', this._mouseUp);
            MM.removeEvent(document, 'mousemove', this._mouseMove);

            var angle = Math.atan2(
                this.lastMouse.y - this.prevMouse.y,
                this.lastMouse.x - this.prevMouse.x);
            var distance = MM.Point.distance(this.lastMouse, this.prevMouse);
            var speed = Math.min(Math.log(1 + (distance / ((+new Date()) - this.prevMouse.t))) * 90, 300);

            if (isNaN(angle)) return;

            var center = this.map.coordinatePoint(this.map.coordinate);
            var pan = this.map.pointLocation(new MM.Point(
                center.x + (Math.cos(angle) * speed),
                center.y + (Math.sin(angle) * speed)));

            easey.slow(this.map, {
                pan: pan
            }, speed);
            this.prevMouse = null;
            this.map.parent.style.cursor = '';

            return MM.cancelEvent(e);
        }
    };

    this.easey = easey;
})(this, com.modestmaps);
<|endoftext|>


<commit_before>/**
 * Author: Olaolu Olawuyi

 * Date Created: 20/08/2015

 * Last Modified: 18/03/2016

 * Twitter: @whizkydee, @webtwic

 * Github: https://github.com/webtwic/webtwic-site

*/

var Webtwic = Webtwic || {};

var browser = bowser;

var test_canvas = document.createElement( "canvas" );
var canvascheck = ( test_canvas.getContext ) ? true : false;

Webtwic = {
	// Basic Information about Webtwic
	name: "Webtwic",
	description: "A community for coding and tech tweaks where newbies and experts can learn.",
	repo: "https://github.com/webtwic/webtwic-site",
	author: "Olaolu Olawuyi",
	version: 1.0,

	// Start main functions for the object

	// Function to add browser name and version to body class
	showBrowser: function( b ) {
		$( "body" ).addClass( b + " " + b + parseInt( browser.version ) );
	},

	// Function to set attributes and values to elements
	setAttr: function( elem, name, val ) {
		$( elem, this ).attr( name, val ) ;
	},

	// Function to push down .site-content
	adjustSiteContent: function() {
		if ( $( "body" ).hasClass( "default-body" ) ) {
			$( ".top-stripe" ).addClass( "adjustedMargin" );
			$( "#header .logo" ).addClass( "adjustedTop" );
			$( ".main-menu" ).addClass( "adjustedMargin" );
			$( "#header" ).addClass( "adjustSiteContent" );
		} else if ( $( "body" ).hasClass( "secondary-body" ) ) {
			$( ".site-content" ).addClass( "adjustSiteContent" );
			$( ".secondary-header" ).addClass( "adjustedMargin" );
		}
	},

	// Function to push up .site-content to default state
	undoSiteAdjust: function() {
		if ( $( "body" ).hasClass( "default-body" ) ) {
			$( ".top-stripe" ).removeClass( "adjustedMargin" );
			$( "#header .logo" ).removeClass( "adjustedTop" );
			$( ".main-menu" ).removeClass( "adjustedMargin" );
			$( "#header" ).removeClass( "adjustSiteContent" );
		} else if ( $( "body" ).hasClass( "secondary-body" ) ) {
			$( ".secondary-header" ).removeClass( "adjustedMargin" );
		}
	},

	// Function to show Notifications before header
	showNotif: function( text ) {
		text = ( typeof text == "undefined" ) ? notif_text : text;
		Webtwic.adjustSiteContent();
		$( "body" ).prepend(
			"
"+text+""+close_button+"
"
		);

		$( ".close-button" ).on( "click", function( e ) {
			e.preventDefault();
			Webtwic.undoReset();
		});
	},

	// Function to hide Notifications
	hideNotif: function() {
		$( "#notifBanner" ).removeClass( "notif_vis" );
	},

	resetSite: function() {
		Webtwic.showNotif();
		Webtwic.adjustSiteContent();
	},

	undoReset: function() {
		Webtwic.hideNotif();
		Webtwic.undoSiteAdjust();
	},
}, WBT = Webtwic,

$$ = Webtwic,

close_button = "";

notif_text = "Some features which affect the functionality of webtwic are disabled in your browser. Please try refreshing the page or get a better browser.";

// Detect when the DOM is ready

jQuery( document ).ready( function() {
	// Test for HTML5 support
	if ( window.Modernizr ) {
		if ( !Modernizr.canvas ) {
			Webtwic.showNotif ( "Oops. Looks like your browser doesn't support HTML5. Please get a better browser for an awesome experience." );
		} else {
			// Do nothing.
		}
	} else if ( !window.Modernizr ) {
		if ( canvascheck == false ) {
			Webtwic.showNotif ( "Oops. Looks like your browser doesn't support HTML5. Please get a better browser for an awesome experience." );
		} else {
			// Do nothing.
		}
	} else {
		// Do nothing.
	}

	// Define Variables
	var $scroll_top = $( "a.scroll_top" ),
	is_toggle_active = $( ".hamburger" ).hasClass( "is_active" ),
	height = 200;

	// Performance Fixes

	// Start browser detection

	browser.ie = browser.msie;

	if ( browser.webkit ) {
		$( "body" ).addClass( "webkit" );
	}
	if ( browser.chrome ) {
		Webtwic.showBrowser( "chrome" );
	}
	if ( browser.msedge ) {
		Webtwic.showBrowser( "msedge" );
	}
	if ( browser.firefox ) {
		Webtwic.showBrowser( "firefox" );
	}
	if ( browser.msie ) {
		Webtwic.showBrowser( "ie" );
	}
	if ( browser.safari ) {
		Webtwic.showBrowser( "safari" );
	}
	if ( browser.opera ) {
		Webtwic.showBrowser( "opera" );
	}

	/*
		TODO: Rebuild Performance fixes for attributes
		See history of Performance fixes.
		Also, check StackOverflow.
		See Issue #12
	*/

	if ( window.Modernizr ) {
		if ( !Modernizr.flexbox || !Modernizr.inlinesvg || !Modernizr.svg || !Modernizr.svgclippaths || !Modernizr.fontface || !Modernizr.boxshadow || !Modernizr.borderradius || !Modernizr.csstransforms ) {
			Webtwic.showNotif();
		} else {
			// Do nothing.
		}
	} else {
		// Do nothing.
	}

	// Start Main Page

	$( "nav li ul.drop-nav" ).hide().removeClass( "dropNavFallback" );

	if ( $( window ).width() > 860 ) {
		$( "nav li.dropdown" ).hover(function() {
			$( "ul.drop-nav", this ).stop().slideDown( "fast" );
		}, function() {
			$( "ul.drop-nav", this ).stop().slideUp( "fast" );
		});
	}

	$( ".active-item" ).append(
		"<svg viewBox='0 0 32 32' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'><path d='M 16.682,19.674c 0.010-0.012, 0.014-0.028, 0.024-0.040l 6.982-7.714c 0.39-0.434, 0.39-1.138,0-1.572 c-0.004-0.004-0.008-0.006-0.012-0.008C 23.496,10.13, 23.244,10, 22.964,10L 8.998,10 c-0.286,0-0.54,0.138-0.722,0.352L 8.272,10.348 c-0.39,0.434-0.39,1.138,0,1.572l 6.998,7.754C 15.66,20.108, 16.292,20.108, 16.682,19.674z'></path></svg>"
	);

	$( "a.hamburger" ).click(function( e ) {
		$( this ).toggleClass( "is_active" );

		e.preventDefault();
	});
	$( ".mobile-header .mobile-nav" ).hide();

	$( window ).scroll(function() {
		( $( this ).scrollTop() > height ) ? $scroll_top.addClass( "topVisible" ): $scroll_top.removeClass( "topVisible" );
	});

	// Styles for homepage article-social

	$( ".article .article-social a" ).hover(function() {
		$( "span", this ).eq(1).fadeIn(400);
	}, function() {
		$( "span", this ).eq(1).fadeOut(250);
	});

	// Functions for search-icon and ui-overlay-search

	$( ".header-follow-search .search-wrap" ).on( "click", function() {
		openUiOverlay();
	});

	$( ".header-follow-search .close-icon" ).on( "click", function( e ) {
		e.preventDefault();
		closeUiOverlay();
	});

	$( ".header-follow-search .close-icon" ).hover(
		function() {
			$( this ).addClass( "spin" );
		},
		function() {
			$( this ).removeClass( "spin" );
	});

	function openUiOverlay() {
		$( ".ui-overlay-search" ).fadeIn(400).addClass( "is_open" );
	}

	function closeUiOverlay() {
		$( ".ui-overlay-search" ).fadeOut(400).removeClass( "is_open" );
	}

	$( ".ui-overlay-search .search-input input" ).focus(function() {
		$( ".search-input" ).attr( "data-focused", true );
		$( ".search-input input" ).addClass( "_is-focused" );
		$( "#__expand" ).addClass( "visible" );
	});

	$( ".ui-overlay-search .search-input input" ).blur(function() {
		$( ".search-input" ).attr( "data-focused", false );
		$( ".search-input input" ).removeClass( "_is-focused" );
		$( "#__expand" ).removeClass( "visible" );
	});

	$( ".post-meta .post-meta-inner .cta-btns a.is_inactive" ).on( "click", function( e ) {
		e.preventDefault();
	});

	$( document ).keydown(function( e ) {
		if ( e.keyCode == 27 ) {
			closeUiOverlay();
		} else {
			// Do nothing.
		}
 	});

	$( ".mobile-nav" ).hide();

	// Functions for more-posts button click

	function addLoader() {
		$( ".secondary-category-section .category-posts-column [data-clicked='false']" ).attr( "data-clicked", "true" ).append( "" );
	}

	$( ".secondary-category-section .category-posts-column [data-clicked='false']" ).on( "click", function( e ) {
		$( this ).css({
			"background": "#50ABF1",
			"color": "#fff"
		});
		addLoader();
		e.preventDefault();
	});

	$scroll_top.on( "click", function( e ) {
		$( "body, html" ).animate({
			scrollTop: 0
		}, 1000);

		e.preventDefault();
	});

});
<commit_msg>Update ui-overlay search input data-focused attr<commit_after>/**
 * Author: Olaolu Olawuyi

 * Date Created: 20/08/2015

 * Last Modified: 23/03/2016

 * Twitter: @whizkydee, @webtwic

 * Github: https://github.com/webtwic/webtwic-site

*/

var Webtwic = Webtwic || {};

var browser = bowser;

var test_canvas = document.createElement( "canvas" );
var canvascheck = ( test_canvas.getContext ) ? true : false;

Webtwic = {
	// Basic Information about Webtwic
	name: "Webtwic",
	description: "A community for coding and tech tweaks where newbies and experts can learn.",
	repo: "https://github.com/webtwic/webtwic-site",
	author: "Olaolu Olawuyi",
	version: 1.0,

	// Start main functions for the object

	// Function to add browser name and version to body class
	showBrowser: function( b ) {
		$( "body" ).addClass( b + " " + b + parseInt( browser.version ) );
	},

	// Function to set attributes and values to elements
	setAttr: function( elem, name, val ) {
		$( elem, this ).attr( name, val ) ;
	},

	// Function to push down .site-content
	adjustSiteContent: function() {
		if ( $( "body" ).hasClass( "default-body" ) ) {
			$( ".top-stripe" ).addClass( "adjustedMargin" );
			$( "#header .logo" ).addClass( "adjustedTop" );
			$( ".main-menu" ).addClass( "adjustedMargin" );
			$( "#header" ).addClass( "adjustSiteContent" );
		} else if ( $( "body" ).hasClass( "secondary-body" ) ) {
			$( ".site-content" ).addClass( "adjustSiteContent" );
			$( ".secondary-header" ).addClass( "adjustedMargin" );
		}
	},

	// Function to push up .site-content to default state
	undoSiteAdjust: function() {
		if ( $( "body" ).hasClass( "default-body" ) ) {
			$( ".top-stripe" ).removeClass( "adjustedMargin" );
			$( "#header .logo" ).removeClass( "adjustedTop" );
			$( ".main-menu" ).removeClass( "adjustedMargin" );
			$( "#header" ).removeClass( "adjustSiteContent" );
		} else if ( $( "body" ).hasClass( "secondary-body" ) ) {
			$( ".secondary-header" ).removeClass( "adjustedMargin" );
		}
	},

	// Function to show Notifications before header
	showNotif: function( text ) {
		text = ( typeof text == "undefined" ) ? notif_text : text;
		Webtwic.adjustSiteContent();
		$( "body" ).prepend(
			"
"+text+""+close_button+"
"
		);

		$( ".close-button" ).on( "click", function( e ) {
			e.preventDefault();
			Webtwic.undoReset();
		});
	},

	// Function to hide Notifications
	hideNotif: function() {
		$( "#notifBanner" ).removeClass( "notif_vis" );
	},

	resetSite: function() {
		Webtwic.showNotif();
		Webtwic.adjustSiteContent();
	},

	undoReset: function() {
		Webtwic.hideNotif();
		Webtwic.undoSiteAdjust();
	},
}, WBT = Webtwic,

$$ = Webtwic,

close_button = "";

notif_text = "Some features which affect the functionality of webtwic are disabled in your browser. Please try refreshing the page or get a better browser.";

// Detect when the DOM is ready

jQuery( document ).ready( function() {
	// Test for HTML5 support
	if ( window.Modernizr ) {
		if ( !Modernizr.canvas ) {
			Webtwic.showNotif ( "Oops. Looks like your browser doesn't support HTML5. Please get a better browser for an awesome experience." );
		} else {
			// Do nothing.
		}
	} else if ( !window.Modernizr ) {
		if ( canvascheck == false ) {
			Webtwic.showNotif ( "Oops. Looks like your browser doesn't support HTML5. Please get a better browser for an awesome experience." );
		} else {
			// Do nothing.
		}
	} else {
		// Do nothing.
	}

	// Define Variables
	var $scroll_top = $( "a.scroll_top" ),
	is_toggle_active = $( ".hamburger" ).hasClass( "is_active" ),
	height = 200;

	// Performance Fixes

	// Start browser detection

	browser.ie = browser.msie;

	if ( browser.webkit ) {
		$( "body" ).addClass( "webkit" );
	}
	if ( browser.chrome ) {
		Webtwic.showBrowser( "chrome" );
	}
	if ( browser.msedge ) {
		Webtwic.showBrowser( "msedge" );
	}
	if ( browser.firefox ) {
		Webtwic.showBrowser( "firefox" );
	}
	if ( browser.msie ) {
		Webtwic.showBrowser( "ie" );
	}
	if ( browser.safari ) {
		Webtwic.showBrowser( "safari" );
	}
	if ( browser.opera ) {
		Webtwic.showBrowser( "opera" );
	}

	/*
		TODO: Rebuild Performance fixes for attributes
		See history of Performance fixes.
		Also, check StackOverflow.
		See Issue #12
	*/

	if ( window.Modernizr ) {
		if ( !Modernizr.flexbox || !Modernizr.inlinesvg || !Modernizr.svg || !Modernizr.svgclippaths || !Modernizr.fontface || !Modernizr.boxshadow || !Modernizr.borderradius || !Modernizr.csstransforms ) {
			Webtwic.showNotif();
		} else {
			// Do nothing.
		}
	} else {
		// Do nothing.
	}

	// Start Main Page

	$( "nav li ul.drop-nav" ).hide().removeClass( "dropNavFallback" );

	if ( $( window ).width() > 860 ) {
		$( "nav li.dropdown" ).hover(function() {
			$( "ul.drop-nav", this ).stop().slideDown( "fast" );
		}, function() {
			$( "ul.drop-nav", this ).stop().slideUp( "fast" );
		});
	}

	$( ".active-item" ).append(
		"<svg viewBox='0 0 32 32' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'><path d='M 16.682,19.674c 0.010-0.012, 0.014-0.028, 0.024-0.040l 6.982-7.714c 0.39-0.434, 0.39-1.138,0-1.572 c-0.004-0.004-0.008-0.006-0.012-0.008C 23.496,10.13, 23.244,10, 22.964,10L 8.998,10 c-0.286,0-0.54,0.138-0.722,0.352L 8.272,10.348 c-0.39,0.434-0.39,1.138,0,1.572l 6.998,7.754C 15.66,20.108, 16.292,20.108, 16.682,19.674z'></path></svg>"
	);

	$( "a.hamburger" ).click(function( e ) {
		$( this ).toggleClass( "is_active" );

		e.preventDefault();
	});
	$( ".mobile-header .mobile-nav" ).hide();

	$( window ).scroll(function() {
		( $( this ).scrollTop() > height ) ? $scroll_top.addClass( "topVisible" ): $scroll_top.removeClass( "topVisible" );
	});

	// Styles for homepage article-social

	$( ".article .article-social a" ).hover(function() {
		$( "span", this ).eq(1).fadeIn(400);
	}, function() {
		$( "span", this ).eq(1).fadeOut(250);
	});

	// Functions for search-icon and ui-overlay-search

	$( ".header-follow-search .search-wrap" ).on( "click", function() {
		openUiOverlay();
	});

	$( ".header-follow-search .close-icon" ).on( "click", function( e ) {
		e.preventDefault();
		closeUiOverlay();
	});

	$( ".header-follow-search .close-icon" ).hover(
		function() {
			$( this ).addClass( "spin" );
		},
		function() {
			$( this ).removeClass( "spin" );
	});

	function openUiOverlay() {
		$( ".ui-overlay-search" ).fadeIn(400).addClass( "is_open" );
	}

	function closeUiOverlay() {
		$( ".ui-overlay-search" ).fadeOut(400).removeClass( "is_open" );
	}

	$( ".ui-overlay-search .search-input input" ).focus(function() {
		$( ".search-input" ).attr( "data-focused", "true" );
		$( ".search-input input" ).addClass( "_is-focused" );
		$( "#__expand" ).addClass( "visible" );
	});

	$( ".ui-overlay-search .search-input input" ).blur(function() {
		$( ".search-input" ).attr( "data-focused", "false" );
		$( ".search-input input" ).removeClass( "_is-focused" );
		$( "#__expand" ).removeClass( "visible" );
	});

	$( ".post-meta .post-meta-inner .cta-btns a.is_inactive" ).on( "click", function( e ) {
		e.preventDefault();
	});

	$( document ).keydown(function( e ) {
		if ( e.keyCode == 27 ) {
			closeUiOverlay();
		} else {
			// Do nothing.
		}
 	});

	$( ".mobile-nav" ).hide();

	// Functions for more-posts button click

	function addLoader() {
		$( ".secondary-category-section .category-posts-column [data-clicked='false']" ).attr( "data-clicked", "true" ).append( "" );
	}

	$( ".secondary-category-section .category-posts-column [data-clicked='false']" ).on( "click", function( e ) {
		$( this ).css({
			"background": "#50ABF1",
			"color": "#fff"
		});
		addLoader();
		e.preventDefault();
	});

	$scroll_top.on( "click", function( e ) {
		$( "body, html" ).animate({
			scrollTop: 0
		}, 1000);

		e.preventDefault();
	});

});
<|endoftext|>