<commit_before>/* global chrome, fetch, Worker, localStorage, importScripts, formatter, linter */

const WORKER_API_AVAILABLE = typeof Worker != "undefined";
const LOCAL_STORAGE_API_AVAILABLE = typeof localStorage != "undefined";
const MENU_ID_COPY_PATH = "copy-path";
const MENU_ID_COPY_VALUE = "copy-value";
const MENU_ID_COPY_JSON_VALUE = "copy-json-value";

if (!WORKER_API_AVAILABLE) {
	importScripts("/js/workers/formatter.js");
	importScripts("/js/workers/linter.js");
}

let extensionReady;
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
	onMessage(message).then(sendResponse);
	return true;
});
chrome.contextMenus.onClicked.addListener((info, tab) => chrome.tabs.sendMessage(tab.id, { copy: true, type: info.menuItemId }));
addMenuEntry(true);
init();

async function init() {
	extensionReady = migrateSettings();
	const settings = await getSettings();
	await initDefaultSettings(settings);
	await refreshMenuEntry();
}

async function initDefaultSettings(settings) {
	if (!settings.options) {
		settings.options = {};
	}
	if (typeof settings.options.maxDepthLevelExpanded == "undefined") {
		settings.options.maxDepthLevelExpanded = 0;
		await setSetting("options", settings.options);
	}
	if (typeof settings.options.addContextMenu == "undefined") {
		settings.options.addContextMenu = true;
		await setSetting("options", settings.options);
	}
	if (!settings.theme) {
		const theme = await getDefaultTheme();
		await setSetting("theme", theme);
	}
}

async function migrateSettings() {
	const promises = [];
	if (LOCAL_STORAGE_API_AVAILABLE) {
		if (localStorage.options) {
			promises.push(new Promise(resolve => {
				chrome.storage.local.set({ options: JSON.parse(localStorage.options) }, () => resolve());
				delete localStorage.options;
			}));
		}
		if (localStorage.theme) {
			promises.push(new Promise(resolve => {
				chrome.storage.local.set({ theme: localStorage.theme }, () => resolve());
				delete localStorage.theme;
			}));
		}
	}
	await Promise.all(promises);
}

async function onMessage(message) {
	const json = message.json;
	if (message.setSetting) {
		await setSetting(message.name, message.value);
		return {};
	}
	if (message.getSettings) {
		const settings = await getSettings();
		return settings;
	}
	if (message.refreshMenuEntry) {
		await refreshMenuEntry();
		return {};
	}
	if (message.init) {
		return {
			options: (await getSettings()).options || {},
			theme: await getTheme()
		};
	}
	if (message.jsonToHTML) {
		const result = await formatHTML(json, message.functionName, message.offset);
		return result;
	}
}

async function formatHTML(json, functionName, offset) {
	if (WORKER_API_AVAILABLE) {
		const response = await executeWorker("js/workers/formatter.js", { json: json, functionName });
		if (response.html) {
			return { html: response.html };
		}
		if (response.error) {
			const message = await executeWorker("js/workers/linter.js", json);
			return { error: message.error, loc: message.loc, offset };
		}
	} else {
		try {
			const html = formatter.format(json, functionName);
			return { html };
		} catch (error) {
			const result = linter.lint(json);
			return { error: result.error, loc: result.loc, offset };
		}
	}
}

function executeWorker(path, message) {
	return new Promise((resolve, reject) => {
		const worker = new Worker(path);
		worker.addEventListener("message", onMessage, false);
		worker.addEventListener("error", onError, false);
		worker.postMessage(message);

		function onMessage(event) {
			worker.removeEventListener("message", onMessage, false);
			worker.removeEventListener("error", onError, false);
			worker.terminate();
			resolve(event.data);
		}

		function onError(event) {
			reject(event.detail.error);
		}
	});
}

async function refreshMenuEntry() {
	const settings = await getSettings();
	const options = (settings).options;
	chrome.contextMenus.removeAll();
	if (options.addContextMenu) {
		addMenuEntry();
	}
}

function addMenuEntry(removeAll) {
	if (removeAll) {
		chrome.contextMenus.removeAll();
	}
	chrome.contextMenus.create({
		id: MENU_ID_COPY_PATH,
		title: "Copy path",
		contexts: ["page", "link"]
	});
	chrome.contextMenus.create({
		id: MENU_ID_COPY_VALUE,
		title: "Copy value",
		contexts: ["page", "link"]
	});
	chrome.contextMenus.create({
		id: MENU_ID_COPY_JSON_VALUE,
		title: "Copy JSON value",
		contexts: ["page", "link"]
	});
}

async function getDefaultTheme() {
	return (await fetch("/css/jsonvue.css")).text();
}

async function getTheme() {
	return (await Promise.all([
		(await fetch("/css/jsonvue-error.css")).text(),
		(await fetch("/css/jsonvue-core.css")).text(),
		(await getSettings()).theme
	])).join("\n");
}

async function getSettings() {
	await extensionReady;
	return new Promise(resolve => chrome.storage.local.get(["options", "theme"], result => resolve(result)));
}

async function setSetting(name, value) {
	await extensionReady;
	return new Promise(resolve => chrome.storage.local.set({ [name]: value }, result => resolve(result)));
}<commit_msg>remove useless variables<commit_after>/* global chrome, fetch, Worker, localStorage, importScripts, formatter, linter */

const WORKER_API_AVAILABLE = typeof Worker != "undefined";
const LOCAL_STORAGE_API_AVAILABLE = typeof localStorage != "undefined";
const MENU_ID_COPY_PATH = "copy-path";
const MENU_ID_COPY_VALUE = "copy-value";
const MENU_ID_COPY_JSON_VALUE = "copy-json-value";

if (!WORKER_API_AVAILABLE) {
	importScripts("/js/workers/formatter.js");
	importScripts("/js/workers/linter.js");
}

let extensionReady;
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
	onMessage(message).then(sendResponse);
	return true;
});
chrome.contextMenus.onClicked.addListener((info, tab) => chrome.tabs.sendMessage(tab.id, { copy: true, type: info.menuItemId }));
addMenuEntry(true);
init();

async function init() {
	extensionReady = migrateSettings();
	const settings = await getSettings();
	await initDefaultSettings(settings);
	await refreshMenuEntry();
}

async function initDefaultSettings(settings) {
	if (!settings.options) {
		settings.options = {};
	}
	if (typeof settings.options.maxDepthLevelExpanded == "undefined") {
		settings.options.maxDepthLevelExpanded = 0;
		await setSetting("options", settings.options);
	}
	if (typeof settings.options.addContextMenu == "undefined") {
		settings.options.addContextMenu = true;
		await setSetting("options", settings.options);
	}
	if (!settings.theme) {
		await setSetting("theme", await getDefaultTheme());
	}
}

async function migrateSettings() {
	const promises = [];
	if (LOCAL_STORAGE_API_AVAILABLE) {
		if (localStorage.options) {
			promises.push(new Promise(resolve => {
				chrome.storage.local.set({ options: JSON.parse(localStorage.options) }, () => resolve());
				delete localStorage.options;
			}));
		}
		if (localStorage.theme) {
			promises.push(new Promise(resolve => {
				chrome.storage.local.set({ theme: localStorage.theme }, () => resolve());
				delete localStorage.theme;
			}));
		}
	}
	await Promise.all(promises);
}

async function onMessage(message) {
	const json = message.json;
	if (message.setSetting) {
		await setSetting(message.name, message.value);
		return {};
	}
	if (message.getSettings) {
		return getSettings();
	}
	if (message.refreshMenuEntry) {
		await refreshMenuEntry();
		return {};
	}
	if (message.init) {
		return {
			options: (await getSettings()).options || {},
			theme: await getTheme()
		};
	}
	if (message.jsonToHTML) {
		return formatHTML(json, message.functionName, message.offset);
	}
}

async function formatHTML(json, functionName, offset) {
	if (WORKER_API_AVAILABLE) {
		const response = await executeWorker("js/workers/formatter.js", { json: json, functionName });
		if (response.html) {
			return { html: response.html };
		}
		if (response.error) {
			const message = await executeWorker("js/workers/linter.js", json);
			return { error: message.error, loc: message.loc, offset };
		}
	} else {
		try {
			return { html: formatter.format(json, functionName) };
		} catch (error) {
			const result = linter.lint(json);
			return { error: result.error, loc: result.loc, offset };
		}
	}
}

function executeWorker(path, message) {
	return new Promise((resolve, reject) => {
		const worker = new Worker(path);
		worker.addEventListener("message", onMessage, false);
		worker.addEventListener("error", onError, false);
		worker.postMessage(message);

		function onMessage(event) {
			worker.removeEventListener("message", onMessage, false);
			worker.removeEventListener("error", onError, false);
			worker.terminate();
			resolve(event.data);
		}

		function onError(event) {
			reject(event.detail.error);
		}
	});
}

async function refreshMenuEntry() {
	const settings = await getSettings();
	chrome.contextMenus.removeAll();
	if (settings.options.addContextMenu) {
		addMenuEntry();
	}
}

function addMenuEntry(removeAll) {
	if (removeAll) {
		chrome.contextMenus.removeAll();
	}
	chrome.contextMenus.create({
		id: MENU_ID_COPY_PATH,
		title: "Copy path",
		contexts: ["page", "link"]
	});
	chrome.contextMenus.create({
		id: MENU_ID_COPY_VALUE,
		title: "Copy value",
		contexts: ["page", "link"]
	});
	chrome.contextMenus.create({
		id: MENU_ID_COPY_JSON_VALUE,
		title: "Copy JSON value",
		contexts: ["page", "link"]
	});
}

async function getDefaultTheme() {
	return (await fetch("/css/jsonvue.css")).text();
}

async function getTheme() {
	return (await Promise.all([
		(await fetch("/css/jsonvue-error.css")).text(),
		(await fetch("/css/jsonvue-core.css")).text(),
		(await getSettings()).theme
	])).join("\n");
}

async function getSettings() {
	await extensionReady;
	return new Promise(resolve => chrome.storage.local.get(["options", "theme"], result => resolve(result)));
}

async function setSetting(name, value) {
	await extensionReady;
	return new Promise(resolve => chrome.storage.local.set({ [name]: value }, result => resolve(result)));
}<|endoftext|>


<commit_before>var ow = require('1-wire-js');
chrome.app.runtime.onLaunched.addListener(function (launchData) {
	chrome.app.window.create(
		'index.html', {
			id: 'eChipPortalTool',
			state: 'maximized'
		},
		function (appWindow) {
			appWindow.contentWindow.ow = ow;
			appWindow.onClosed.addListener(function () {
				ow.deviceClose();
			});
		});
});
<commit_msg>Fixes error in device close<commit_after>var ow = require('1-wire-js');
chrome.app.runtime.onLaunched.addListener(function (launchData) {
	chrome.app.window.create(
		'index.html', {
			id: 'eChipPortalTool',
			state: 'maximized'
		},
		function (appWindow) {
			appWindow.contentWindow.ow = ow;
			appWindow.onClosed.addListener(function () {
				ow.device.close();
			});
		});
});
<|endoftext|>


<commit_before>/*
Copyright 2013 Weswit s.r.l.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/
function Controller($scope) {
  $scope.items = {
  };

  require(["js/lsClient","Subscription"], function(lsClient,Subscription) {

    var subscription = new Subscription("MERGE",["item1","item2"],["stock_name","last_price"]);
    subscription.setDataAdapter("QUOTE_ADAPTER");

    subscription.addListener({
      onItemUpdate: function(info) {
        var itemName = info.getItemName();
        if (!$scope.items[itemName]) {
          $scope.items[itemName] = {};
        }
        info.forEachChangedField(function(fieldName,fieldPos,val) {
          $scope.items[itemName][fieldName] = val;
        });

        $scope.$apply();
      }
    });

    lsClient.subscribe(subscription);

  });
}  <commit_msg>subscribe to 10 items instead of subscribing to 2 items (the new items automatically shown by angular)<commit_after>/*
Copyright 2013 Weswit s.r.l.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/
function Controller($scope) {
  $scope.items = {
  };

  var items = ["item1","item2","item3","item4","item5","item6","item7","item8","item9","item10"];

  require(["js/lsClient","Subscription"], function(lsClient,Subscription) {

    var subscription = new Subscription("MERGE",items,["stock_name","last_price"]);
    subscription.setDataAdapter("QUOTE_ADAPTER");

    subscription.addListener({
      onItemUpdate: function(info) {
        var itemName = info.getItemName();
        if (!$scope.items[itemName]) {
          $scope.items[itemName] = {};
        }
        info.forEachChangedField(function(fieldName,fieldPos,val) {
          $scope.items[itemName][fieldName] = val;
        });

        $scope.$apply();
      }
    });

    lsClient.subscribe(subscription);

  });
}  <|endoftext|>


<commit_before>export const setLastFocusedWindowId = (lastFocusedWindowId) => {
  chrome.storage.local.set({ lastFocusedWindowId })
}

export const getLastFocusedWindowId = async () => {
  const { lastFocusedWindowId } = await chrome.storage.local.get({ lastFocusedWindowId: null })
  return lastFocusedWindowId
}

const getQueryInfo = (windowId) => {
  if (windowId) {
    return { windowId }
  }
  return { currentWindow: true }
}

export const getActiveTab = async () => {
  const windowId = await getLastFocusedWindowId()
  const queryInfo = getQueryInfo(windowId)
  const tabs = await chrome.tabs.query({ ...queryInfo, active: true })
  if (tabs.length <= 0) {
    // TODO: handle this in UI
    throw new Error('No active tab! This is impossible')
  }
  return tabs[0]
}

const SOURCE_PREFIX = 'data:text/javascript'
const BASE64_PREFIX = SOURCE_PREFIX + ';base64,'
const UTF8_PREFIX = SOURCE_PREFIX + ';charset=utf-8,'

export const encodeSource = (script) => {
  // base64 may be smaller, but does not handle unicode characters
  // attempt base64 first, fall back to escaped text
  try {
    return (BASE64_PREFIX + window.btoa(script))
  } catch (e) {
    return (UTF8_PREFIX + encodeURIComponent(script))
  }
}

export const decodeSource = (source) => {
  if (source.startsWith(BASE64_PREFIX)) {
    return window.atob(source.replace(BASE64_PREFIX, ''))
  }
  if (source.startsWith(UTF8_PREFIX)) {
    return decodeURIComponent(source.replace(UTF8_PREFIX, ''))
  }
  if (!source) {
    return ''
  }
  return source
}

export const getHosts = async (key) => {
  const result = await chrome.storage.sync.get({ hosts: [] })
  if (result.hosts.length > 0) {
    return result.hosts
  }
  const { hosts = [] } = JSON.parse(window.localStorage.getItem(key) || '{}')
  return hosts
}

export const setHosts = async (hosts = []) => {
  chrome.storage.sync.set({ hosts })
}

export const clearHosts = () => {
  chrome.storage.sync.remove('hosts')
}
<commit_msg>Add extra check for hosts in chrome.storage.sync<commit_after>export const setLastFocusedWindowId = (lastFocusedWindowId) => {
  chrome.storage.local.set({ lastFocusedWindowId })
}

export const getLastFocusedWindowId = async () => {
  const { lastFocusedWindowId } = await chrome.storage.local.get({ lastFocusedWindowId: null })
  return lastFocusedWindowId
}

const getQueryInfo = (windowId) => {
  if (windowId) {
    return { windowId }
  }
  return { currentWindow: true }
}

export const getActiveTab = async () => {
  const windowId = await getLastFocusedWindowId()
  const queryInfo = getQueryInfo(windowId)
  const tabs = await chrome.tabs.query({ ...queryInfo, active: true })
  if (tabs.length <= 0) {
    // TODO: handle this in UI
    throw new Error('No active tab! This is impossible')
  }
  return tabs[0]
}

const SOURCE_PREFIX = 'data:text/javascript'
const BASE64_PREFIX = SOURCE_PREFIX + ';base64,'
const UTF8_PREFIX = SOURCE_PREFIX + ';charset=utf-8,'

export const encodeSource = (script) => {
  // base64 may be smaller, but does not handle unicode characters
  // attempt base64 first, fall back to escaped text
  try {
    return (BASE64_PREFIX + window.btoa(script))
  } catch (e) {
    return (UTF8_PREFIX + encodeURIComponent(script))
  }
}

export const decodeSource = (source) => {
  if (source.startsWith(BASE64_PREFIX)) {
    return window.atob(source.replace(BASE64_PREFIX, ''))
  }
  if (source.startsWith(UTF8_PREFIX)) {
    return decodeURIComponent(source.replace(UTF8_PREFIX, ''))
  }
  if (!source) {
    return ''
  }
  return source
}

export const getHosts = async (key) => {
  const result = await chrome.storage.sync.get({ hosts: [] })
  if (Array.isArray(result.hosts) && result.hosts.length > 0) {
    return result.hosts
  }
  const { hosts = [] } = JSON.parse(window.localStorage.getItem(key) || '{}')
  return hosts
}

export const setHosts = async (hosts = []) => {
  chrome.storage.sync.set({ hosts })
}

export const clearHosts = () => {
  chrome.storage.sync.remove('hosts')
}
<|endoftext|>


<commit_before>define(["backbone","underscore"], function(Backbone, _) {

	var Model = Backbone.Collection.extend({
		// holds a list of the next members to call if we're using the exclusive randomisation
		orderList: [],

		// similar to the collection function where, only searches the data attribute
		whereData: function(attrs) {
			if (_.isEmpty(attrs)) {
				return [];
			}
			return this.filter(function(model) {
				var data = model.get('data') || {};
				for (var key in attrs) {
					if (attrs[key] !== data[key]){
                        return false;
                    }
				}
                return true;
			});
		},

		// plain randomization
		random: function(){
			return this.at(Math.floor(Math.random()*this.length)).attributes;
		},

		// randomize without repeat
		exRandom: function(){
			this.orderList = this.orderList.length ? this.orderList : _.shuffle(_.range(this.length));
			return this.at(this.orderList.pop()).attributes;
		},

		// find model by data attributes
		// check if all attributes of the handle appear in the model data
		// if the handle is not an abject compare to data.handle
		byData : function(definitions){
			// if a handle object isn't	defined use the keyword "handle"
			var data = _.isEmpty(definitions.data) ? {data:definitions.data} : definitions.data;

			// get the first element that fits the handle
			var element = this.whereData(data)[0];
			if (!element) {
                throw new Error('Inherit by Handle failed. Handle not found: ' + definitions.data);
            }
			return	element.attributes;
		},

		inherit: function(definitions){
			// if this is a function, return it with the set as "this"
			if (_.isFunction(definitions.type)) {
                return definitions.type.call(this,definitions);
            }

			// otherwise call the appropriate built in function
			switch (definitions.type) {
				case 'byData' : return this.byData(definitions);
				case 'exRandom' : return this.exRandom();
				case 'random' :
                    /* falls through */
				default:
					return this.random();
			}
		}

	});

	// Returns the Model class
	return Model;
});<commit_msg>Added by sequence inheritance<commit_after>define(["backbone","underscore"], function(Backbone, _) {

	var Model = Backbone.Collection.extend({
		// holds a list of the next members to call if we're using the exclusive randomisation
		orderList: [],
		// the next pick for the bySequence function
		nextPick: 0,

		// similar to the collection function where, only searches the data attribute
		whereData: function(attrs) {
			if (_.isEmpty(attrs)) {
				return [];
			}
			return this.filter(function(model) {
				var data = model.get('data') || {};
				for (var key in attrs) {
					if (attrs[key] !== data[key]){
                        return false;
                    }
				}
                return true;
			});
		},

		// plain randomization
		random: function(){
			return this.at(Math.floor(Math.random()*this.length)).attributes;
		},

		// randomize without repeat
		exRandom: function(){
			this.orderList = this.orderList.length ? this.orderList : _.shuffle(_.range(this.length));
			return this.at(this.orderList.pop()).attributes;
		},

		bySequence: function(){
			// reset the nextPick pointer if needed
			this.nextPick < this.length || (this.nextPick = 0);
			return this.at(this.nextPick);
		},

		// find model by data attributes
		// check if all attributes of the handle appear in the model data
		// if the handle is not an abject compare to data.handle
		byData : function(definitions){
			// if a handle object isn't	defined use the keyword "handle"
			var data = _.isEmpty(definitions.data) ? {data:definitions.data} : definitions.data;

			// get the first element that fits the handle
			var element = this.whereData(data)[0];
			if (!element) {
                throw new Error('Inherit by Handle failed. Handle not found: ' + definitions.data);
            }
			return	element.attributes;
		},

		inherit: function(definitions){
			// if this is a function, return it with the set as "this"
			if (_.isFunction(definitions.type)) {
                return definitions.type.call(this,definitions);
            }

			// otherwise call the appropriate built in function
			switch (definitions.type) {
				case 'bySequence' : return this.bySequence();
				case 'byData' : return this.byData(definitions);
				case 'exRandom' : return this.exRandom();
				case 'random' :
                    /* falls through */
				default:
					return this.random();
			}
		}

	});

	// Returns the Model class
	return Model;
});<|endoftext|>


<commit_before>import THREE from 'three';

import Tree, {
  Base,
  TrapezoidalPrism,
  EquilateralTriangularPrism,
  Pyramid
} from './../tree/tree';

function render3d() {
  const renderer = new THREE.WebGLRenderer({ antialias: true });
  renderer.setPixelRatio( window.devicePixelRatio );
  renderer.setSize( window.innerWidth, window.innerHeight );
  document.body.appendChild( renderer.domElement );

  const scene = new THREE.Scene();

  const camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight );
  const cameraRadius = 8;
  camera.position.set( 0, 0, cameraRadius );
  scene.add( camera );

  scene.add( new THREE.AmbientLight( '#222' ) );

  const light = new THREE.DirectionalLight( '#fff' );
  light.position.set( 0, 4, 4 );
  scene.add( light );

  // Geometry.
  const base = new Base( 2 );
  const trapezoid = new TrapezoidalPrism( 1, 1.5, 1.5 );
  const triangle = new EquilateralTriangularPrism( 1, 1 );
  const trapezoidA = new TrapezoidalPrism( 0.75, 1.5, 0.5 );
  const pyramid = new Pyramid( 5, 1 );
  const pyramidA = new Pyramid( 2, 1 );

  base.add( trapezoid );
  trapezoid.add( triangle );
  triangle.add( trapezoidA, 'left' );
  triangle.add( pyramid, 'right' );
  trapezoidA.add( pyramidA );

  const geometry = new THREE.Geometry();
  geometry.bones = [];

  base.traverse( object => {
    const offset = geometry.vertices.length;
    const tempGeometry = object.createGeometry( offset );
    geometry.vertices.push( ...tempGeometry.vertices );
    geometry.faces.push( ...tempGeometry.faces );
    object.createBone( geometry );
  });

  geometry.computeFaceNormals();
  geometry.computeVertexNormals();

  const material = new THREE.MeshPhongMaterial({
    // wireframe: true,
    skinning: true,
    shading: THREE.FlatShading
  });

  const mesh = new THREE.SkinnedMesh( geometry, material );
  scene.add( mesh );

  const skeletonHelper = new THREE.SkeletonHelper( mesh );
  skeletonHelper.material.linewidth = 4;
  mesh.add( skeletonHelper );

  function animate() {
    // Rotate camera instead of mesh to prevent problems with SkeletonHelper
    // transforms.
    const time = Date.now() * 1e-3;
    camera.position.x = cameraRadius * Math.cos( time );
    camera.position.z = cameraRadius * Math.sin( time );
    camera.lookAt( mesh.position );
    skeletonHelper.update();

    renderer.render( scene, camera );
    requestAnimationFrame( animate );
  }

  animate();
}

function render2d() {
  const canvas = document.createElement( 'canvas' );
  const ctx    = canvas.getContext( '2d' );

  const size = 640;
  canvas.width  = size;
  canvas.height = size;
  document.body.appendChild( canvas );

  function drawPolygon( ctx, vertices ) {
    if ( !vertices.length ) {
      return;
    }

    ctx.beginPath();

    ctx.moveTo( vertices[0][0], vertices[0][1] );
    for ( let i = 1, il = vertices.length; i < il; i++ ) {
      ctx.lineTo( vertices[i][0], vertices[i][1] );
    }

    ctx.closePath();

    ctx.fill();
    ctx.stroke();
  }

  // Bottom-heavy trapezoid.
  function createTrapezoid( bottomWidth, topWidth, height ) {
    const halfBottomWidth = bottomWidth / 2;
    const halfTopWidth    = topWidth    / 2;

    // Counter-clockwise from bottom-left.
    const vertices = [
      [ -halfBottomWidth, 0      ],
      [  halfBottomWidth, 0      ],
      [  halfTopWidth,    height ],
      [ -halfTopWidth,    height ]
    ];

    return {
      vertices,

      draw( ctx ) {
        drawPolygon( ctx, vertices );
      },

      transform( ctx ) {
        ctx.save();
        ctx.translate( 0, height );
      }
    };
  }

  function createEquilateralTriangle( length ) {
    const halfLength = length / 2;

    const height = Math.sqrt( 3 ) * halfLength;
    const halfHeight = height / 2;

    // Counter-clockwise from bottom-left.
    const vertices = [
      [ -halfLength, 0      ],
      [  halfLength, 0      ],
      [  0,          height ]
    ];

    return {
      vertices,

      draw( ctx ) {
        drawPolygon( ctx, vertices );
      },

      transformLeft( ctx ) {
        ctx.save();
        // Move to side bisector.
        ctx.translate( -halfLength / 2, halfHeight );
        ctx.rotate( THREE.Math.degToRad( 60 ) );
      },

      transformRight( ctx ) {
        ctx.save();
        ctx.translate( halfLength / 2, halfHeight );
        ctx.rotate( THREE.Math.degToRad( -60 ) );
      }
    };
  }

  // Branches.
  function createIsocelesTriangle( width, height ) {
    const halfWidth = width / 2;

    const vertices = [
      [ -halfWidth, 0      ],
      [  halfWidth, 0      ],
      [  0,         height ]
    ];

    return {
      vertices,

      draw( ctx ) {
        drawPolygon( ctx, vertices );
      }
    };
  }

  const trapA = createTrapezoid( 120, 70, 120 );
  const triA  = createEquilateralTriangle( 70 );
  const triB  = createIsocelesTriangle( 70, 180 );
  const trapB = createTrapezoid( 70, 30, 80 );
  const triC  = createEquilateralTriangle( 30 );
  const triD  = createIsocelesTriangle( 30, 100 );

  // Center and flip y-axis.
  ctx.translate( size / 2, size / 2 );
  ctx.scale( 1, -1 );

  ctx.fillStyle = '#fff';
  ctx.strokeStyle = '#333';
  trapA.draw( ctx );

  trapA.transform( ctx );
  triA.draw( ctx );

  triA.transformLeft( ctx );
  triB.draw( ctx );
  ctx.restore();

  triA.transformRight( ctx );
  trapB.draw( ctx );

  trapB.transform( ctx );
  triC.draw( ctx );

  triC.transformRight( ctx );
  triD.draw( ctx );
}

export default function() {
  render3d();
}
<commit_msg>tree: Add basic growth animation.<commit_after>import THREE from 'three';

import Tree, {
  Base,
  TrapezoidalPrism,
  EquilateralTriangularPrism,
  Pyramid
} from './../tree/tree';

function render3d() {
  const renderer = new THREE.WebGLRenderer({ antialias: true });
  renderer.setPixelRatio( window.devicePixelRatio );
  renderer.setSize( window.innerWidth, window.innerHeight );
  document.body.appendChild( renderer.domElement );

  const scene = new THREE.Scene();

  const camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight );
  const cameraRadius = 8;
  camera.position.set( 0, 0, cameraRadius );
  scene.add( camera );

  scene.add( new THREE.AmbientLight( '#222' ) );

  const light = new THREE.DirectionalLight( '#fff' );
  light.position.set( 0, 4, 4 );
  scene.add( light );

  // Geometry.
  const base = new Base( 2 );
  const trapezoid = new TrapezoidalPrism( 1, 1.5, 1.5 );
  const triangle = new EquilateralTriangularPrism( 1, 1 );

  const pyramidRight = new Pyramid( 5 );

  const trapezoidLeft = new TrapezoidalPrism( 0.75, 1.5, 0.5 );
  const triangleLeft = new EquilateralTriangularPrism( 1, 1 );
  const pyramidLeftLeft = new Pyramid( 2 );
  const pyramidLeftRight = new Pyramid( 3 );

  base.add( trapezoid );
  trapezoid.add( triangle );
  triangle.add( trapezoidLeft, 'left' );
  triangle.add( pyramidRight, 'right' );
  trapezoidLeft.add( triangleLeft );
  triangleLeft.add( pyramidLeftLeft, 'left' );
  triangleLeft.add( pyramidLeftRight, 'right' );

  const geometry = new THREE.Geometry();
  geometry.bones = [];

  base.traverse( object => {
    const offset = geometry.vertices.length;
    const tempGeometry = object.createGeometry( offset );
    geometry.vertices.push( ...tempGeometry.vertices );
    geometry.faces.push( ...tempGeometry.faces );
    object.createBone( geometry );
  });

  geometry.computeFaceNormals();
  geometry.computeVertexNormals();

  const material = new THREE.MeshPhongMaterial({
    // wireframe: true,
    skinning: true,
    shading: THREE.FlatShading
  });

  const mesh = new THREE.SkinnedMesh( geometry, material );
  scene.add( mesh );

  const skeletonHelper = new THREE.SkeletonHelper( mesh );
  skeletonHelper.material.linewidth = 4;
  mesh.add( skeletonHelper );

  // Traverse bones and mark direction based on relative x-axis position.
  mesh.skeleton.bones.forEach( bone => {
    const { parent } = bone;
    if ( !parent ) {
      return;
    }

    const grandparent = parent.parent;
    if ( !grandparent ) {
      if ( bone.position.x < parent.position.x ) { bone.direction = 'left';  }
      if ( bone.position.x > parent.position.x ) { bone.direction = 'right'; }
      return;
    }

    const { x, y } = bone.position;
    const { x: x1, y: y1 } = parent.position;
    const { x: x0, y: y0 } = grandparent.position;

    const cross = ( x - x0 ) * ( y1 - y0 ) - ( y - y0 ) * ( x1 - x0 );

    if ( cross < 0 ) { bone.direction = 'left';  }
    if ( cross > 0 ) { bone.direction = 'right'; }
  });

  const scale = new THREE.Vector3();

  function animate() {
    // Rotate camera instead of mesh to prevent problems with SkeletonHelper
    // transforms.
    const time = Date.now() * 1e-3;
    camera.position.x = cameraRadius * Math.cos( time );
    camera.position.z = cameraRadius * Math.sin( time );
    camera.lookAt( mesh.position );

    const cos = Math.cos( time );

    const angle  = 0.5 * ( cos - 1 ) * Math.PI / 2;
    const length = 0.5 * ( cos + 1 ) * 2;

    mesh.skeleton.bones.forEach( ( bone, index ) => {
      scale.setFromMatrixScale( bone.parent.matrixWorld );
      bone.scale.setLength( length / scale.length() );

      // Set angle if not base.
      if ( index > 1 ) {
        if ( bone.direction === 'left'  ) { bone.rotation.z =  angle; }
        if ( bone.direction === 'right' ) { bone.rotation.z = -angle; }
      }

      bone.updateMatrixWorld();
    });

    skeletonHelper.update();
    renderer.render( scene, camera );
    requestAnimationFrame( animate );
  }

  animate();
}

function render2d() {
  const canvas = document.createElement( 'canvas' );
  const ctx    = canvas.getContext( '2d' );

  const size = 640;
  canvas.width  = size;
  canvas.height = size;
  document.body.appendChild( canvas );

  function drawPolygon( ctx, vertices ) {
    if ( !vertices.length ) {
      return;
    }

    ctx.beginPath();

    ctx.moveTo( vertices[0][0], vertices[0][1] );
    for ( let i = 1, il = vertices.length; i < il; i++ ) {
      ctx.lineTo( vertices[i][0], vertices[i][1] );
    }

    ctx.closePath();

    ctx.fill();
    ctx.stroke();
  }

  // Bottom-heavy trapezoid.
  function createTrapezoid( bottomWidth, topWidth, height ) {
    const halfBottomWidth = bottomWidth / 2;
    const halfTopWidth    = topWidth    / 2;

    // Counter-clockwise from bottom-left.
    const vertices = [
      [ -halfBottomWidth, 0      ],
      [  halfBottomWidth, 0      ],
      [  halfTopWidth,    height ],
      [ -halfTopWidth,    height ]
    ];

    return {
      vertices,

      draw( ctx ) {
        drawPolygon( ctx, vertices );
      },

      transform( ctx ) {
        ctx.save();
        ctx.translate( 0, height );
      }
    };
  }

  function createEquilateralTriangle( length ) {
    const halfLength = length / 2;

    const height = Math.sqrt( 3 ) * halfLength;
    const halfHeight = height / 2;

    // Counter-clockwise from bottom-left.
    const vertices = [
      [ -halfLength, 0      ],
      [  halfLength, 0      ],
      [  0,          height ]
    ];

    return {
      vertices,

      draw( ctx ) {
        drawPolygon( ctx, vertices );
      },

      transformLeft( ctx ) {
        ctx.save();
        // Move to side bisector.
        ctx.translate( -halfLength / 2, halfHeight );
        ctx.rotate( THREE.Math.degToRad( 60 ) );
      },

      transformRight( ctx ) {
        ctx.save();
        ctx.translate( halfLength / 2, halfHeight );
        ctx.rotate( THREE.Math.degToRad( -60 ) );
      }
    };
  }

  // Branches.
  function createIsocelesTriangle( width, height ) {
    const halfWidth = width / 2;

    const vertices = [
      [ -halfWidth, 0      ],
      [  halfWidth, 0      ],
      [  0,         height ]
    ];

    return {
      vertices,

      draw( ctx ) {
        drawPolygon( ctx, vertices );
      }
    };
  }

  const trapA = createTrapezoid( 120, 70, 120 );
  const triA  = createEquilateralTriangle( 70 );
  const triB  = createIsocelesTriangle( 70, 180 );
  const trapB = createTrapezoid( 70, 30, 80 );
  const triC  = createEquilateralTriangle( 30 );
  const triD  = createIsocelesTriangle( 30, 100 );

  // Center and flip y-axis.
  ctx.translate( size / 2, size / 2 );
  ctx.scale( 1, -1 );

  ctx.fillStyle = '#fff';
  ctx.strokeStyle = '#333';
  trapA.draw( ctx );

  trapA.transform( ctx );
  triA.draw( ctx );

  triA.transformLeft( ctx );
  triB.draw( ctx );
  ctx.restore();

  triA.transformRight( ctx );
  trapB.draw( ctx );

  trapB.transform( ctx );
  triC.draw( ctx );

  triC.transformRight( ctx );
  triD.draw( ctx );
}

export default function() {
  render3d();
}
<|endoftext|>


<commit_before>var echarts = require('echarts/lib/echarts');
var layoutUtil = require('echarts/lib/util/layout');
var graphicGL = require('../../util/graphicGL');
var ViewGL = require('../../core/ViewGL');
var Lines2DGeometry = require('../../util/geometry/Lines2D');
var retrieve = require('../../util/retrieve');
var ForceAtlas2GPU = require('./ForceAtlas2GPU');
var ForceAtlas2 = require('./ForceAtlas2');
var requestAnimationFrame = require('zrender/lib/animation/requestAnimationFrame');
var vec2 = require('qtek/lib/dep/glmatrix').vec2;

var Roam2DControl = require('../../util/Roam2DControl');

var PointsBuilder = require('../common/PointsBuilder');

graphicGL.Shader.import(require('text!../../util/shader/lines2D.glsl'));

var globalLayoutId = 1;

echarts.extendChartView({

    type: 'graphGL',

    __ecgl__: true,

    init: function (ecModel, api) {

        this.groupGL = new graphicGL.Node();
        this.viewGL = new ViewGL('orthographic');

        this.viewGL.add(this.groupGL);

        this._pointsBuilder = new PointsBuilder(true, api);

        // Mesh used during force directed layout.
        this._forceEdgesMesh = new graphicGL.Mesh({
            material: new graphicGL.Material({
                shader: graphicGL.createShader('ecgl.forceAtlas2.edges'),
                transparent: true,
                depthMask: false,
                depthTest: false
            }),
            geometry: new graphicGL.Geometry({
                attributes: {
                    node: new graphicGL.Geometry.Attribute('node', 'float', 2),
                    color: new graphicGL.Geometry.Attribute('color', 'float', 4, 'COLOR')
                },
                dynamic: true,
                mainAttribute: 'node'
            }),
            renderOrder: -1,
            mode: graphicGL.Mesh.LINES
        });

        // Mesh used after force directed layout.
        this._edgesMesh = new graphicGL.Mesh({
            material: new graphicGL.Material({
                shader: graphicGL.createShader('ecgl.meshLines2D'),
                transparent: true,
                depthMask: false,
                depthTest: false
            }),
            geometry: new Lines2DGeometry({
                useNativeLine: false,
                dynamic: true
            }),
            culling: false
        });

        this._layoutId = 0;

        this._control = new Roam2DControl({
            zr: api.getZr(),
            viewGL: this.viewGL
        });
        this._control.setTarget(this.groupGL);
        this._control.init();
    },

    render: function (seriesModel, ecModel, api) {
        this.groupGL.add(this._pointsBuilder.rootNode);

        this._model = seriesModel;
        this._api = api;

        this._initLayout(seriesModel, ecModel, api);

        this._pointsBuilder.update(seriesModel, ecModel, api);

        this._updateForceNodesGeometry(seriesModel.getData());

        if (!(this._forceLayoutInstance instanceof ForceAtlas2GPU)) {
            this.groupGL.remove(this._forceEdgesMesh);
        }

        this._updateCamera(seriesModel, api);

        this._control.off('update');
        this._control.on('update', function () {
                api.dispatchAction({
                    type: 'graphGLRoam',
                    seriesId: seriesModel.id,
                    zoom: this._control.getZoom(),
                    offset: this._control.getOffset()
                });
            }, this);

        this._control.setZoom(retrieve.firstNotNull(seriesModel.get('zoom'), 1));
        this._control.setOffset(seriesModel.get('offset') || [0, 0]);
    },

    _updateForceEdgesGeometry: function (edges, seriesModel) {
        var geometry = this._forceEdgesMesh.geometry;

        var edgeData = seriesModel.getEdgeData();
        var offset = 0;
        var layoutInstance = this._forceLayoutInstance;
        var vertexCount = edgeData.count() * 2;
        geometry.attributes.node.init(vertexCount);
        geometry.attributes.color.init(vertexCount);
        edgeData.each(function (idx) {
            var edge = edges[idx];
            geometry.attributes.node.set(offset, layoutInstance.getNodeUV(edge.node1));
            geometry.attributes.node.set(offset + 1, layoutInstance.getNodeUV(edge.node2));

            var color = edgeData.getItemVisual(edge.dataIndex, 'color');
            var colorArr = graphicGL.parseColor(color);
            colorArr[3] *= retrieve.firstNotNull(
                edgeData.getItemVisual(edge.dataIndex, 'opacity'), 1
            );
            geometry.attributes.color.set(offset, colorArr);
            geometry.attributes.color.set(offset + 1, colorArr);

            offset += 2;
        });
        geometry.dirty();
    },

    _updateEdgesGeometry: function (edges) {

        var geometry = this._edgesMesh.geometry;
        var edgeData = this._model.getEdgeData();
        var points = this._model.getData().getLayout('points');

        geometry.resetOffset();
        geometry.setVertexCount(edges.length * geometry.getLineVertexCount());
        geometry.setTriangleCount(edges.length * geometry.getLineTriangleCount());

        var p0 = [];
        var p1 = [];

        var lineWidthQuery = ['lineStyle', 'width'];
        for (var i = 0; i < edges.length; i++) {
            var edge = edges[i];
            var idx1 = edge.node1 * 2;
            var idx2 = edge.node2 * 2;
            p0[0] = points[idx1];
            p0[1] = points[idx1 + 1];
            p1[0] = points[idx2];
            p1[1] = points[idx2 + 1];

            var color = edgeData.getItemVisual(i, 'color');
            var colorArr = graphicGL.parseColor(color);
            colorArr[3] *= retrieve.firstNotNull(
                edgeData.getItemVisual(i, 'opacity'), 1
            );
            var itemModel = edgeData.getItemModel(i);
            var lineWidth = retrieve.firstNotNull(itemModel.get(lineWidthQuery), 1) * this._api.getDevicePixelRatio();

            geometry.addLine(p0, p1, colorArr, lineWidth);
        }

        geometry.dirty();
    },

    _updateForceNodesGeometry: function (nodeData) {
        var pointsMesh = this._pointsBuilder.getPointsMesh();
        var pos = [];
        for (var i = 0; i < nodeData.count(); i++) {
            pointsMesh.geometry.attributes.position.get(i, pos);
            this._forceLayoutInstance.getNodeUV(i, pos);
            pointsMesh.geometry.attributes.position.set(i, pos);
        }
        pointsMesh.geometry.dirty();
    },

    _initLayout: function (seriesModel, ecModel, api) {
        var layout = seriesModel.get('layout');
        var graph = seriesModel.getGraph();

        var boxLayoutOption = seriesModel.getBoxLayoutParams();
        var viewport = layoutUtil.getLayoutRect(boxLayoutOption, {
            width: api.getWidth(),
            height: api.getHeight()
        });

        if (layout === 'force') {
            if (__DEV__) {
                console.warn('Currently only forceAtlas2 layout supported.');
            }
            layout = 'forceAtlas2';
        }
        // Stop previous layout
        this.stopLayout();

        var nodeData = seriesModel.getData();
        var edgeData = seriesModel.getData();
        if (layout === 'forceAtlas2') {
            var layoutModel = seriesModel.getModel('forceAtlas2');
            var layoutInstance = this._forceLayoutInstance;
            var nodes = [];
            var edges = [];

            var nodeDataExtent = nodeData.getDataExtent('value');
            var edgeDataExtent = edgeData.getDataExtent('value');

            var edgeWeightRange = retrieve.firstNotNull(layoutModel.get('edgeWeight'), 1.0);
            var nodeWeightRange = retrieve.firstNotNull(layoutModel.get('nodeWeight'), 1.0);
            if (typeof edgeWeightRange === 'number') {
                edgeWeightRange = [edgeWeightRange, edgeWeightRange];
            }
            if (typeof nodeWeightRange === 'number') {
                nodeWeightRange = [nodeWeightRange, nodeWeightRange];
            }

            var offset = 0;
            var nodesIndicesMap = {};

            var layoutPoints = new Float32Array(nodeData.count() * 2);
            graph.eachNode(function (node) {
                var dataIndex = node.dataIndex;
                var value = nodeData.get('value', dataIndex);
                var x;
                var y;
                if (nodeData.hasItemOption) {
                    var itemModel = nodeData.getItemModel(dataIndex);
                    x = itemModel.get('x');
                    y = itemModel.get('y');
                }
                if (x == null) {
                    // Random in rectangle
                    x = viewport.x + Math.random() * viewport.width;
                    y = viewport.y + Math.random() * viewport.height;
                }
                layoutPoints[offset * 2] = x;
                layoutPoints[offset * 2 + 1] = y;

                nodesIndicesMap[node.id] = offset++;
                var mass = echarts.number.linearMap(value, nodeDataExtent, nodeWeightRange);
                if (isNaN(mass)) {
                    if (!isNaN(nodeWeightRange[0])) {
                        mass = nodeWeightRange[0];
                    }
                    else {
                        mass = 1;
                    }
                }
                nodes.push({
                    x: x, y: y, mass: mass
                });
            });
            nodeData.setLayout('points', layoutPoints);

            graph.eachEdge(function (edge) {
                var dataIndex = edge.dataIndex;
                var value = nodeData.get('value', dataIndex);
                var weight = echarts.number.linearMap(value, edgeDataExtent, edgeWeightRange);
                if (isNaN(weight)) {
                    if (!isNaN(edgeWeightRange[0])) {
                        weight = edgeWeightRange[0];
                    }
                    else {
                        weight = 1;
                    }
                }
                edges.push({
                    node1: nodesIndicesMap[edge.node1.id],
                    node2: nodesIndicesMap[edge.node2.id],
                    weight: weight
                });
            });
            if (!layoutInstance) {
                var isGPU = layoutModel.get('GPU');
                if (this._forceLayoutInstance) {
                    if ((isGPU && !(this._forceLayoutInstance instanceof ForceAtlas2GPU))
                        || (!isGPU && !(this._forceLayoutInstance instanceof ForceAtlas2))
                    ) {
                        // Mark to dispose
                        this._forceLayoutInstanceToDispose = this._forceLayoutInstance;
                    }
                }
                layoutInstance = this._forceLayoutInstance = isGPU
                    ? new ForceAtlas2GPU()
                    : new ForceAtlas2();
            }
            layoutInstance.initData(nodes, edges);
            layoutInstance.updateOption(layoutModel.option);

            // Update lines geometry after first layout;
            this._updateForceEdgesGeometry(layoutInstance.getEdges(), seriesModel);
            this._updatePositionTexture();

            api.dispatchAction({
                type: 'graphGLStartLayout'
            });
        }
        else {
            var layoutPoints = new Float32Array(nodeData.count() * 2);
            graph.eachNode(function (node) {
                var dataIndex = node.dataIndex;
                var x;
                var y;
                if (nodeData.hasItemOption) {
                    var itemModel = nodeData.getItemModel(dataIndex);
                    x = itemModel.get('x');
                    y = itemModel.get('y');
                }
                layoutPoints[offset * 2] = x;
                layoutPoints[offset * 2 + 1] = y;
            });
            nodeData.setLayout('points', layoutPoints);

            // TODO
        }
    },

    startLayout: function () {
        var viewGL = this.viewGL;
        var api = this._api;
        var layoutInstance = this._forceLayoutInstance;
        var layoutModel = this._model.getModel('forceAtlas2');

        this.groupGL.remove(this._edgesMesh);
        this.groupGL.add(this._forceEdgesMesh);

        if (!this._forceLayoutInstance) {
            return;
        }

        var self = this;
        var layoutId = this._layoutId = globalLayoutId++;
        var stopThreshold = layoutModel.getShallow('stopThreshold');
        var steps = layoutModel.getShallow('steps');
        var doLayout = function (layoutId) {
            if (layoutId !== self._layoutId) {
                return;
            }
            if (layoutInstance.isFinished(viewGL.layer.renderer, stopThreshold)) {
                api.dispatchAction({
                    type: 'graphGLStopLayout'
                });
                return;
            }

            layoutInstance.update(viewGL.layer.renderer, steps, function () {
                self._updatePositionTexture();
                // Position texture will been swapped. set every time.
                api.getZr().refresh();

                requestAnimationFrame(function () {
                    doLayout(layoutId);
                });
            });
        };

        requestAnimationFrame(function () {
            if (self._forceLayoutInstanceToDispose) {
                self._forceLayoutInstanceToDispose.dispose(viewGL.layer.renderer);
                self._forceLayoutInstanceToDispose = null;
            }
            doLayout(layoutId);
        });
    },

    _updatePositionTexture: function () {
        var positionTex = this._forceLayoutInstance.getNodePositionTexture();
        this._pointsBuilder.setPositionTexture(positionTex);
        this._forceEdgesMesh.material.set('positionTex', positionTex);
    },

    stopLayout: function () {
        this._layoutId = 0;
        this.groupGL.remove(this._forceEdgesMesh);
        this.groupGL.add(this._edgesMesh);

        if (!this._forceLayoutInstance) {
            return;
        }
        var points = this._forceLayoutInstance.getNodePosition(this.viewGL.layer.renderer);

        this._model.getData().setLayout('points', points);

        this._updateEdgesGeometry(this._forceLayoutInstance.getEdges());
    },

    _updateCamera: function (seriesModel, api) {
        this.viewGL.setViewport(0, 0, api.getWidth(), api.getHeight(), api.getDevicePixelRatio());
        var camera = this.viewGL.camera;
        var nodeData = seriesModel.getData();
        var points = nodeData.getLayout('points');
        var min = vec2.create(Infinity, Infinity);
        var max = vec2.create(-Infinity, -Infinity);
        var pt = [];
        for (var i = 0; i < points.length;) {
            pt[0] = points[i++];
            pt[1] = points[i++];
            vec2.min(min, min, pt);
            vec2.max(max, max, pt);
        }
        // Scale a bit
        var width = max[0] - min[0];
        var height = max[1] - min[1];
        width *= 1.4;
        height *= 1.4;
        min[0] -= width * 0.2;
        min[1] -= height * 0.2;

        camera.left = min[0];
        camera.top = min[1];
        camera.bottom = height + min[1];
        camera.right = width + min[0];
        camera.near = 0;
        camera.far = 100;
    },

    dispose: function () {
        var renderer = this.viewGL.layer.renderer;
        if (this._forceLayoutInstance) {
            this._forceLayoutInstance.dispose(renderer);
        }
        this.groupGL.removeAll();

        this.stopLayout();
    },

    remove: function () {
        this.groupGL.removeAll();
        this._control.dispose();
    }
});<commit_msg>Tweak<commit_after>var echarts = require('echarts/lib/echarts');
var layoutUtil = require('echarts/lib/util/layout');
var graphicGL = require('../../util/graphicGL');
var ViewGL = require('../../core/ViewGL');
var Lines2DGeometry = require('../../util/geometry/Lines2D');
var retrieve = require('../../util/retrieve');
var ForceAtlas2GPU = require('./ForceAtlas2GPU');
var ForceAtlas2 = require('./ForceAtlas2');
var requestAnimationFrame = require('zrender/lib/animation/requestAnimationFrame');
var vec2 = require('qtek/lib/dep/glmatrix').vec2;

var Roam2DControl = require('../../util/Roam2DControl');

var PointsBuilder = require('../common/PointsBuilder');

graphicGL.Shader.import(require('text!../../util/shader/lines2D.glsl'));

var globalLayoutId = 1;

echarts.extendChartView({

    type: 'graphGL',

    __ecgl__: true,

    init: function (ecModel, api) {

        this.groupGL = new graphicGL.Node();
        this.viewGL = new ViewGL('orthographic');

        this.viewGL.add(this.groupGL);

        this._pointsBuilder = new PointsBuilder(true, api);

        // Mesh used during force directed layout.
        this._forceEdgesMesh = new graphicGL.Mesh({
            material: new graphicGL.Material({
                shader: graphicGL.createShader('ecgl.forceAtlas2.edges'),
                transparent: true,
                depthMask: false,
                depthTest: false
            }),
            geometry: new graphicGL.Geometry({
                attributes: {
                    node: new graphicGL.Geometry.Attribute('node', 'float', 2),
                    color: new graphicGL.Geometry.Attribute('color', 'float', 4, 'COLOR')
                },
                dynamic: true,
                mainAttribute: 'node'
            }),
            renderOrder: -1,
            mode: graphicGL.Mesh.LINES
        });

        // Mesh used after force directed layout.
        this._edgesMesh = new graphicGL.Mesh({
            material: new graphicGL.Material({
                shader: graphicGL.createShader('ecgl.meshLines2D'),
                transparent: true,
                depthMask: false,
                depthTest: false
            }),
            geometry: new Lines2DGeometry({
                useNativeLine: false,
                dynamic: true
            }),
            culling: false
        });

        this._layoutId = 0;

        this._control = new Roam2DControl({
            zr: api.getZr(),
            viewGL: this.viewGL
        });
        this._control.setTarget(this.groupGL);
        this._control.init();
    },

    render: function (seriesModel, ecModel, api) {
        this.groupGL.add(this._pointsBuilder.rootNode);

        this._model = seriesModel;
        this._api = api;

        this._initLayout(seriesModel, ecModel, api);

        this._pointsBuilder.update(seriesModel, ecModel, api);

        this._updateForceNodesGeometry(seriesModel.getData());

        if (!(this._forceLayoutInstance instanceof ForceAtlas2GPU)) {
            this.groupGL.remove(this._forceEdgesMesh);
        }

        this._updateCamera(seriesModel, api);

        this._control.off('update');
        this._control.on('update', function () {
                api.dispatchAction({
                    type: 'graphGLRoam',
                    seriesId: seriesModel.id,
                    zoom: this._control.getZoom(),
                    offset: this._control.getOffset()
                });
            }, this);

        this._control.setZoom(retrieve.firstNotNull(seriesModel.get('zoom'), 1));
        this._control.setOffset(seriesModel.get('offset') || [0, 0]);
    },

    _updateForceEdgesGeometry: function (edges, seriesModel) {
        var geometry = this._forceEdgesMesh.geometry;

        var edgeData = seriesModel.getEdgeData();
        var offset = 0;
        var layoutInstance = this._forceLayoutInstance;
        var vertexCount = edgeData.count() * 2;
        geometry.attributes.node.init(vertexCount);
        geometry.attributes.color.init(vertexCount);
        edgeData.each(function (idx) {
            var edge = edges[idx];
            geometry.attributes.node.set(offset, layoutInstance.getNodeUV(edge.node1));
            geometry.attributes.node.set(offset + 1, layoutInstance.getNodeUV(edge.node2));

            var color = edgeData.getItemVisual(edge.dataIndex, 'color');
            var colorArr = graphicGL.parseColor(color);
            colorArr[3] *= retrieve.firstNotNull(
                edgeData.getItemVisual(edge.dataIndex, 'opacity'), 1
            );
            geometry.attributes.color.set(offset, colorArr);
            geometry.attributes.color.set(offset + 1, colorArr);

            offset += 2;
        });
        geometry.dirty();
    },

    _updateEdgesGeometry: function (edges) {

        var geometry = this._edgesMesh.geometry;
        var edgeData = this._model.getEdgeData();
        var points = this._model.getData().getLayout('points');

        geometry.resetOffset();
        geometry.setVertexCount(edges.length * geometry.getLineVertexCount());
        geometry.setTriangleCount(edges.length * geometry.getLineTriangleCount());

        var p0 = [];
        var p1 = [];

        var lineWidthQuery = ['lineStyle', 'width'];
        for (var i = 0; i < edges.length; i++) {
            var edge = edges[i];
            var idx1 = edge.node1 * 2;
            var idx2 = edge.node2 * 2;
            p0[0] = points[idx1];
            p0[1] = points[idx1 + 1];
            p1[0] = points[idx2];
            p1[1] = points[idx2 + 1];

            var color = edgeData.getItemVisual(i, 'color');
            var colorArr = graphicGL.parseColor(color);
            colorArr[3] *= retrieve.firstNotNull(
                edgeData.getItemVisual(i, 'opacity'), 1
            );
            var itemModel = edgeData.getItemModel(i);
            var lineWidth = retrieve.firstNotNull(itemModel.get(lineWidthQuery), 1) * this._api.getDevicePixelRatio();

            geometry.addLine(p0, p1, colorArr, lineWidth);
        }

        geometry.dirty();
    },

    _updateForceNodesGeometry: function (nodeData) {
        var pointsMesh = this._pointsBuilder.getPointsMesh();
        var pos = [];
        for (var i = 0; i < nodeData.count(); i++) {
            pointsMesh.geometry.attributes.position.get(i, pos);
            this._forceLayoutInstance.getNodeUV(i, pos);
            pointsMesh.geometry.attributes.position.set(i, pos);
        }
        pointsMesh.geometry.dirty();
    },

    _initLayout: function (seriesModel, ecModel, api) {
        var layout = seriesModel.get('layout');
        var graph = seriesModel.getGraph();

        var boxLayoutOption = seriesModel.getBoxLayoutParams();
        var viewport = layoutUtil.getLayoutRect(boxLayoutOption, {
            width: api.getWidth(),
            height: api.getHeight()
        });

        if (layout === 'force') {
            if (__DEV__) {
                console.warn('Currently only forceAtlas2 layout supported.');
            }
            layout = 'forceAtlas2';
        }
        // Stop previous layout
        this.stopLayout();

        var nodeData = seriesModel.getData();
        var edgeData = seriesModel.getData();
        if (layout === 'forceAtlas2') {
            var layoutModel = seriesModel.getModel('forceAtlas2');
            var layoutInstance = this._forceLayoutInstance;
            var nodes = [];
            var edges = [];

            var nodeDataExtent = nodeData.getDataExtent('value');
            var edgeDataExtent = edgeData.getDataExtent('value');

            var edgeWeightRange = retrieve.firstNotNull(layoutModel.get('edgeWeight'), 1.0);
            var nodeWeightRange = retrieve.firstNotNull(layoutModel.get('nodeWeight'), 1.0);
            if (typeof edgeWeightRange === 'number') {
                edgeWeightRange = [edgeWeightRange, edgeWeightRange];
            }
            if (typeof nodeWeightRange === 'number') {
                nodeWeightRange = [nodeWeightRange, nodeWeightRange];
            }

            var offset = 0;
            var nodesIndicesMap = {};

            var layoutPoints = new Float32Array(nodeData.count() * 2);
            graph.eachNode(function (node) {
                var dataIndex = node.dataIndex;
                var value = nodeData.get('value', dataIndex);
                var x;
                var y;
                if (nodeData.hasItemOption) {
                    var itemModel = nodeData.getItemModel(dataIndex);
                    x = itemModel.get('x');
                    y = itemModel.get('y');
                }
                if (x == null) {
                    // Random in rectangle
                    x = viewport.x + Math.random() * viewport.width;
                    y = viewport.y + Math.random() * viewport.height;
                }
                layoutPoints[offset * 2] = x;
                layoutPoints[offset * 2 + 1] = y;

                nodesIndicesMap[node.id] = offset++;
                var mass = echarts.number.linearMap(value, nodeDataExtent, nodeWeightRange);
                if (isNaN(mass)) {
                    if (!isNaN(nodeWeightRange[0])) {
                        mass = nodeWeightRange[0];
                    }
                    else {
                        mass = 1;
                    }
                }
                nodes.push({
                    x: x, y: y, mass: mass
                });
            });
            nodeData.setLayout('points', layoutPoints);

            graph.eachEdge(function (edge) {
                var dataIndex = edge.dataIndex;
                var value = nodeData.get('value', dataIndex);
                var weight = echarts.number.linearMap(value, edgeDataExtent, edgeWeightRange);
                if (isNaN(weight)) {
                    if (!isNaN(edgeWeightRange[0])) {
                        weight = edgeWeightRange[0];
                    }
                    else {
                        weight = 1;
                    }
                }
                edges.push({
                    node1: nodesIndicesMap[edge.node1.id],
                    node2: nodesIndicesMap[edge.node2.id],
                    weight: weight
                });
            });
            if (!layoutInstance) {
                var isGPU = layoutModel.get('GPU');
                if (this._forceLayoutInstance) {
                    if ((isGPU && !(this._forceLayoutInstance instanceof ForceAtlas2GPU))
                        || (!isGPU && !(this._forceLayoutInstance instanceof ForceAtlas2))
                    ) {
                        // Mark to dispose
                        this._forceLayoutInstanceToDispose = this._forceLayoutInstance;
                    }
                }
                layoutInstance = this._forceLayoutInstance = isGPU
                    ? new ForceAtlas2GPU()
                    : new ForceAtlas2();
            }
            layoutInstance.initData(nodes, edges);
            layoutInstance.updateOption(layoutModel.option);

            // Update lines geometry after first layout;
            this._updateForceEdgesGeometry(layoutInstance.getEdges(), seriesModel);
            this._updatePositionTexture();

            api.dispatchAction({
                type: 'graphGLStartLayout'
            });
        }
        else {
            var layoutPoints = new Float32Array(nodeData.count() * 2);
            graph.eachNode(function (node) {
                var dataIndex = node.dataIndex;
                var x;
                var y;
                if (nodeData.hasItemOption) {
                    var itemModel = nodeData.getItemModel(dataIndex);
                    x = itemModel.get('x');
                    y = itemModel.get('y');
                }
                layoutPoints[offset * 2] = x;
                layoutPoints[offset * 2 + 1] = y;
            });
            nodeData.setLayout('points', layoutPoints);

            // TODO
        }
    },

    startLayout: function () {
        var viewGL = this.viewGL;
        var api = this._api;
        var layoutInstance = this._forceLayoutInstance;
        var layoutModel = this._model.getModel('forceAtlas2');

        this.groupGL.remove(this._edgesMesh);
        this.groupGL.add(this._forceEdgesMesh);

        if (!this._forceLayoutInstance) {
            return;
        }

        var self = this;
        var layoutId = this._layoutId = globalLayoutId++;
        var stopThreshold = layoutModel.getShallow('stopThreshold');
        var steps = layoutModel.getShallow('steps');
        var doLayout = function (layoutId) {
            if (layoutId !== self._layoutId) {
                return;
            }
            if (layoutInstance.isFinished(viewGL.layer.renderer, stopThreshold)) {
                api.dispatchAction({
                    type: 'graphGLStopLayout'
                });
                return;
            }

            layoutInstance.update(viewGL.layer.renderer, steps, function () {
                self._updatePositionTexture();
                // Position texture will been swapped. set every time.
                api.getZr().refresh();

                requestAnimationFrame(function () {
                    doLayout(layoutId);
                });
            });
        };

        requestAnimationFrame(function () {
            if (self._forceLayoutInstanceToDispose) {
                self._forceLayoutInstanceToDispose.dispose(viewGL.layer.renderer);
                self._forceLayoutInstanceToDispose = null;
            }
            doLayout(layoutId);
        });
    },

    _updatePositionTexture: function () {
        var positionTex = this._forceLayoutInstance.getNodePositionTexture();
        this._pointsBuilder.setPositionTexture(positionTex);
        this._forceEdgesMesh.material.set('positionTex', positionTex);
    },

    stopLayout: function () {
        this._layoutId = 0;
        this.groupGL.remove(this._forceEdgesMesh);
        this.groupGL.add(this._edgesMesh);

        if (!this._forceLayoutInstance) {
            return;
        }

        if (!this.viewGL.layer) {
            return;
        }

        var points = this._forceLayoutInstance.getNodePosition(this.viewGL.layer.renderer);

        this._model.getData().setLayout('points', points);

        this._updateEdgesGeometry(this._forceLayoutInstance.getEdges());
    },

    _updateCamera: function (seriesModel, api) {
        this.viewGL.setViewport(0, 0, api.getWidth(), api.getHeight(), api.getDevicePixelRatio());
        var camera = this.viewGL.camera;
        var nodeData = seriesModel.getData();
        var points = nodeData.getLayout('points');
        var min = vec2.create(Infinity, Infinity);
        var max = vec2.create(-Infinity, -Infinity);
        var pt = [];
        for (var i = 0; i < points.length;) {
            pt[0] = points[i++];
            pt[1] = points[i++];
            vec2.min(min, min, pt);
            vec2.max(max, max, pt);
        }
        // Scale a bit
        var width = max[0] - min[0];
        var height = max[1] - min[1];
        width *= 1.4;
        height *= 1.4;
        min[0] -= width * 0.2;
        min[1] -= height * 0.2;

        camera.left = min[0];
        camera.top = min[1];
        camera.bottom = height + min[1];
        camera.right = width + min[0];
        camera.near = 0;
        camera.far = 100;
    },

    dispose: function () {
        var renderer = this.viewGL.layer.renderer;
        if (this._forceLayoutInstance) {
            this._forceLayoutInstance.dispose(renderer);
        }
        this.groupGL.removeAll();

        this.stopLayout();
    },

    remove: function () {
        this.groupGL.removeAll();
        this._control.dispose();
    }
});<|endoftext|>


<commit_before>describe("safeList should return a list from native and weird uhc objects", function () {
    it("should return a list from a list", function () {
        expect(Lists.safeList([1,2])).toEqual([1,2]);
    });

    it("should return a list from a uhc list", function () {
        expect(Lists.safeList([[1,2]])).toEqual([1,2]);
    });
});

describe("mapping should apply the signal but not modify it", function () {
    it("should favour undefined to throwing", function () {
        expect(Lists.map()).toBe(undefined);
        expect(Lists.map({__aN__:function(){}, args:[]})).toBe(undefined);
    });

    it("should return undefined if the list isn't a list, or the function isn't a function", function () {
        expect(Lists.map({__aN__:function(){}, args:[]}, {})).toBe(undefined);
        expect(Lists.map([], [])).toBe(undefined);
    });

    it("should return with the same args as given", function () {
        var aSignal = {
            __aN__ : function () { },
            args : [1,2,3]
        };
        Lists.map(aSignal, [1,2,3,4,5])
        expect(aSignal.args).toEqual([1,2,3]);
    });

    it("should apply a function onto all the args", function () {
        var aSignal = {
            __aN__ : function () { return this.args[0]+1 },
            args   : []
        };
        expect(Lists.map(aSignal, [1,2,3])).toEqual([2,3,4]);
    });
});<commit_msg>forgot to push the spec with adjusted lists<commit_after>describe("safeList should return a list from native and weird uhc objects", function () {
    it("should return a list from a list", function () {
        expect(Lists.safeList([1,2])).toEqual([1,2]);
    });

    it("should return a list from a uhc list", function () {
        expect(Lists.safeList([[1,2]])).toEqual([1,2]);
    });
});

describe("mapping should apply the signal but not modify it", function () {
    it("should favour undefined to throwing", function () {
        expect(Lists.map()).toBe(undefined);
        expect(Lists.map({__aN__:function(){}, args:[]})).toBe(undefined);
    });

    it("should return undefined if the list isn't a list, or the function isn't a function", function () {
        expect(Lists.map({__aN__:function(){}, args:[]}, {})).toBe(undefined);
        expect(Lists.map([], [])).toBe(undefined);
    });

    it("should return with the same args as given", function () {
        var aSignal = {
            __aN__ : function () { },
            args : [1,2,3]
        };
        Lists.map(aSignal, [1,2,3,4,5])
        expect(aSignal.args).toEqual([1,2,3]);
    });

    it("should apply a function onto all the args", function () {
        var aSignal = {
            __aN__ : function () { return this.args[0]+1 },
            args   : []
        };
        expect(Lists.fromUHCList(Lists.map(aSignal, [1,2,3]))).toEqual([2,3,4]);
    });
});<|endoftext|>


<commit_before>// RequireJS Handlebars template plugin
// http://github.com/jfparadis/requirejs-handlebars
//
// An alternative to http://github.com/SlexAxton/require-handlebars-plugin/blob/master/hbs.js
//
// Using Handlebars Semantic templates at http://handlebarsjs.com
// Using and RequireJS text.js at http://requirejs.org/docs/api.html#text
// @author JF Paradis
// @version 0.0.2
//
// Released under the MIT license
//
// Usage:
//   require(['backbone', 'hbar!mytemplate'], function (Backbone, mytemplate) {
//     return Backbone.View.extend({
//       initialize: function(){
//         this.render();
//       },
//       render: function(){
//         this.$el.html(mytemplate({message: 'hello'}));
//     });
//   });
//
// Configuration: (optional)
//   require.config({
//     hbars: {
//       extension: '.hbar' // default = '.html'
//     }
//   });

/*jslint nomen: true */
/*global define: false */

define(['text', 'Handlebars'], function (text, Handlebars) {
    'use strict';

    var buildMap = {},
        buildTemplateSource = "define('{pluginName}!{moduleName}', ['Handlebars'], function (Handlebars) { return Handlebars.template({content}); });\n";

    return {
        version: '0.0.2',

        load: function (moduleName, parentRequire, onload, config) {
            if (buildMap[moduleName]) {
                onload(buildMap[moduleName]);

            } else {
                var ext = (config.hbars && config.hbars.extension) || '.html',
                    path = (config.hbars && config.hbars.path) || '',
                    compileOptions = (config.hbars && config.hbars.compileOptions) || {};

                text.load(path + moduleName + ext, parentRequire, function (source) {
                    if (config.isBuild) {
                        // We store the precompiled template so we can use the
                        // handlebars.runtime after build.
                        buildMap[moduleName] = Handlebars.precompile(source, compileOptions);
                        // Don't bother doing anything else during build.
                        onload();
                    } else {
                        // We store the compiled template for reuse
                        buildMap[moduleName] = Handlebars.compile(source);
                        onload(buildMap[moduleName]);
                    }
                }, config);
            }
        },

        write: function (pluginName, moduleName, write, config) {
            var content = buildMap[moduleName];
            if (content) {
                write.asModule(pluginName + '!' + moduleName,
                    buildTemplateSource
                    .replace('{pluginName}', pluginName)
                    .replace('{moduleName}', moduleName)
                    .replace('{content}', content));
            }
        }
    };
});
<commit_msg>updated load call to preserve the onload.error callback.<commit_after>// RequireJS Handlebars template plugin
// http://github.com/jfparadis/requirejs-handlebars
//
// An alternative to http://github.com/SlexAxton/require-handlebars-plugin/blob/master/hbs.js
//
// Using Handlebars Semantic templates at http://handlebarsjs.com
// Using and RequireJS text.js at http://requirejs.org/docs/api.html#text
// @author JF Paradis
// @version 0.0.2
//
// Released under the MIT license
//
// Usage:
//   require(['backbone', 'hbar!mytemplate'], function (Backbone, mytemplate) {
//     return Backbone.View.extend({
//       initialize: function(){
//         this.render();
//       },
//       render: function(){
//         this.$el.html(mytemplate({message: 'hello'}));
//     });
//   });
//
// Configuration: (optional)
//   require.config({
//     hbars: {
//       extension: '.hbar' // default = '.html'
//     }
//   });

/*jslint nomen: true */
/*global define: false */

define(['text', 'Handlebars'], function (text, Handlebars) {
    'use strict';

    var buildMap = {},
        buildTemplateSource = "define('{pluginName}!{moduleName}', ['Handlebars'], function (Handlebars) { return Handlebars.template({content}); });\n";

    return {
        version: '0.0.2',

        load: function (moduleName, parentRequire, onload, config) {
            if (buildMap[moduleName]) {
                onload(buildMap[moduleName]);

            } else {
                var ext = (config.hbars && config.hbars.extension) || '.html',
                    path = (config.hbars && config.hbars.path) || '',
                    compileOptions = (config.hbars && config.hbars.compileOptions) || {},
                    textOnload = function (source) {
                        if (config.isBuild) {
                            // We store the precompiled template so we can use the
                            // handlebars.runtime after build.
                            buildMap[moduleName] = Handlebars.precompile(source, compileOptions);
                            // Don't bother doing anything else during build.
                            onload();
                        } else {
                            // We store the compiled template for reuse
                            buildMap[moduleName] = Handlebars.compile(source);
                            onload(buildMap[moduleName]);
                        }
                    };

                textOnload.error = onload.error;
                text.load(path + moduleName + ext, parentRequire, textOnload, config);
            }
        },

        write: function (pluginName, moduleName, write, config) {
            var content = buildMap[moduleName];
            if (content) {
                write.asModule(pluginName + '!' + moduleName,
                    buildTemplateSource
                    .replace('{pluginName}', pluginName)
                    .replace('{moduleName}', moduleName)
                    .replace('{content}', content));
            }
        }
    };
});
<|endoftext|>


<commit_before>/*
Language: C++
Author: Ivan Sagalaev <maniac@softwaremaniacs.org>
Contributors: Evgeny Stepanischev <imbolk@gmail.com>, Zaven Muradyan <megalivoithos@gmail.com>, Roel Deckers <admin@codingcat.nl>
Category: common, system
*/

function(hljs) {
  var CPP_PRIMITIVE_TYPES = {
    className: 'keyword',
    begin: '\\b[a-z\\d_]*_t\\b'
  };

  var STRINGS = {
    className: 'string',
    variants: [
      {
        begin: '(u8?|U)?L?"', end: '"',
        illegal: '\\n',
        contains: [hljs.BACKSLASH_ESCAPE]
      },
      {
        begin: '(u8?|U)?R"', end: '"',
        contains: [hljs.BACKSLASH_ESCAPE]
      },
      {
        begin: '\'\\\\?.', end: '\'',
        illegal: '.'
      }
    ]
  };

  var NUMBERS = {
    className: 'number',
    variants: [
      { begin: '\\b(\\d+(\\.\\d*)?|\\.\\d+)(u|U|l|L|ul|UL|f|F)' },
      { begin: hljs.C_NUMBER_RE }
    ],
    relevance: 0
  };

  var PREPROCESSOR =       {
    className: 'meta',
    begin: /#[a-z]+\b/, end: /$/,
    keywords: {
      'meta-keyword':
        'if else elif endif define undef warning error line ' +
        'pragma ifdef ifndef include'
    },
    contains: [
      {
        begin: /\\\n/, relevance: 0
      },
      hljs.inherit(STRINGS, {className: 'meta-string'}),
      {
        className: 'meta-string',
        begin: '<', end: '>',
        illegal: '\\n',
      },
      hljs.C_LINE_COMMENT_MODE,
      hljs.C_BLOCK_COMMENT_MODE
    ]
  };

  var FUNCTION_TITLE = hljs.IDENT_RE + '\\s*\\(';

  var CPP_KEYWORDS = {
    keyword: 'int float while private char catch export virtual operator sizeof ' +
      'dynamic_cast|10 typedef const_cast|10 const struct for static_cast|10 union namespace ' +
      'unsigned long volatile static protected bool template mutable if public friend ' +
      'do goto auto void enum else break extern using class asm case typeid ' +
      'short reinterpret_cast|10 default double register explicit signed typename try this ' +
      'switch continue inline delete alignof constexpr decltype ' +
      'noexcept static_assert thread_local restrict _Bool complex _Complex _Imaginary ' +
      'atomic_bool atomic_char atomic_schar ' +
      'atomic_uchar atomic_short atomic_ushort atomic_int atomic_uint atomic_long atomic_ulong atomic_llong ' +
      'atomic_ullong new throw return',
    built_in: 'std string cin cout cerr clog stdin stdout stderr stringstream istringstream ostringstream ' +
      'auto_ptr deque list queue stack vector map set bitset multiset multimap unordered_set ' +
      'unordered_map unordered_multiset unordered_multimap array shared_ptr abort abs acos ' +
      'asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp ' +
      'fscanf isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper ' +
      'isxdigit tolower toupper labs ldexp log10 log malloc realloc memchr memcmp memcpy memset modf pow ' +
      'printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp ' +
      'strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan ' +
      'vfprintf vprintf vsprintf endl initializer_list unique_ptr',
    literal: 'true false nullptr NULL'
  };

  var EXPRESSION_CONTAINS = [
    CPP_PRIMITIVE_TYPES,
    hljs.C_LINE_COMMENT_MODE,
    hljs.C_BLOCK_COMMENT_MODE,
    NUMBERS,
    STRINGS
  ];

  return {
    aliases: ['c', 'cc', 'h', 'c++', 'h++', 'hpp'],
    keywords: CPP_KEYWORDS,
    illegal: '</',
    contains: EXPRESSION_CONTAINS.concat([
      PREPROCESSOR,
      {
        begin: '\\b(deque|list|queue|stack|vector|map|set|bitset|multiset|multimap|unordered_map|unordered_set|unordered_multiset|unordered_multimap|array)\\s*<', end: '>',
        keywords: CPP_KEYWORDS,
        contains: ['self', CPP_PRIMITIVE_TYPES]
      },
      {
        begin: hljs.IDENT_RE + '::',
        keywords: CPP_KEYWORDS
      },
      {
        // This mode covers expression context where we can't expect a function
        // definition and shouldn't highlight anything that looks like one:
        // `return some()`, `else if()`, `(x*sum(1, 2))`
        variants: [
          {begin: /=/, end: /;/},
          {begin: /\(/, end: /\)/},
          {beginKeywords: 'new throw return else', end: /;/}
        ],
        keywords: CPP_KEYWORDS,
        contains: EXPRESSION_CONTAINS.concat([
          {
            begin: /\(/, end: /\)/,
            keywords: CPP_KEYWORDS,
            contains: EXPRESSION_CONTAINS.concat(['self']),
            relevance: 0
          }
        ]),
        relevance: 0
      },
      {
        className: 'function',
        begin: '(' + hljs.IDENT_RE + '[\\*&\\s]+)+' + FUNCTION_TITLE,
        returnBegin: true, end: /[{;=]/,
        excludeEnd: true,
        keywords: CPP_KEYWORDS,
        illegal: /[^\w\s\*&]/,
        contains: [
          {
            begin: FUNCTION_TITLE, returnBegin: true,
            contains: [hljs.TITLE_MODE],
            relevance: 0
          },
          {
            className: 'params',
            begin: /\(/, end: /\)/,
            keywords: CPP_KEYWORDS,
            relevance: 0,
            contains: [
              hljs.C_LINE_COMMENT_MODE,
              hljs.C_BLOCK_COMMENT_MODE,
              STRINGS,
              NUMBERS,
              CPP_PRIMITIVE_TYPES
            ]
          },
          hljs.C_LINE_COMMENT_MODE,
          hljs.C_BLOCK_COMMENT_MODE,
          PREPROCESSOR
        ]
      }
    ]),
    exports: {
      preprocessor: PREPROCESSOR,
      strings: STRINGS,
      keywords: CPP_KEYWORDS
    }
  };
}
<commit_msg>add binary number suffixing to cpp<commit_after>/*
Language: C++
Author: Ivan Sagalaev <maniac@softwaremaniacs.org>
Contributors: Evgeny Stepanischev <imbolk@gmail.com>, Zaven Muradyan <megalivoithos@gmail.com>, Roel Deckers <admin@codingcat.nl>
Category: common, system
*/

function(hljs) {
  var CPP_PRIMITIVE_TYPES = {
    className: 'keyword',
    begin: '\\b[a-z\\d_]*_t\\b'
  };

  var STRINGS = {
    className: 'string',
    variants: [
      {
        begin: '(u8?|U)?L?"', end: '"',
        illegal: '\\n',
        contains: [hljs.BACKSLASH_ESCAPE]
      },
      {
        begin: '(u8?|U)?R"', end: '"',
        contains: [hljs.BACKSLASH_ESCAPE]
      },
      {
        begin: '\'\\\\?.', end: '\'',
        illegal: '.'
      }
    ]
  };

  var NUMBERS = {
    className: 'number',
    variants: [
      { begin: '\\b(\\d+(\\.\\d*)?|\\.\\d+)(u|U|l|L|ul|UL|f|F|b|B)' },
      { begin: hljs.C_NUMBER_RE }
    ],
    relevance: 0
  };

  var PREPROCESSOR =       {
    className: 'meta',
    begin: /#[a-z]+\b/, end: /$/,
    keywords: {
      'meta-keyword':
        'if else elif endif define undef warning error line ' +
        'pragma ifdef ifndef include'
    },
    contains: [
      {
        begin: /\\\n/, relevance: 0
      },
      hljs.inherit(STRINGS, {className: 'meta-string'}),
      {
        className: 'meta-string',
        begin: '<', end: '>',
        illegal: '\\n',
      },
      hljs.C_LINE_COMMENT_MODE,
      hljs.C_BLOCK_COMMENT_MODE
    ]
  };

  var FUNCTION_TITLE = hljs.IDENT_RE + '\\s*\\(';

  var CPP_KEYWORDS = {
    keyword: 'int float while private char catch export virtual operator sizeof ' +
      'dynamic_cast|10 typedef const_cast|10 const struct for static_cast|10 union namespace ' +
      'unsigned long volatile static protected bool template mutable if public friend ' +
      'do goto auto void enum else break extern using class asm case typeid ' +
      'short reinterpret_cast|10 default double register explicit signed typename try this ' +
      'switch continue inline delete alignof constexpr decltype ' +
      'noexcept static_assert thread_local restrict _Bool complex _Complex _Imaginary ' +
      'atomic_bool atomic_char atomic_schar ' +
      'atomic_uchar atomic_short atomic_ushort atomic_int atomic_uint atomic_long atomic_ulong atomic_llong ' +
      'atomic_ullong new throw return',
    built_in: 'std string cin cout cerr clog stdin stdout stderr stringstream istringstream ostringstream ' +
      'auto_ptr deque list queue stack vector map set bitset multiset multimap unordered_set ' +
      'unordered_map unordered_multiset unordered_multimap array shared_ptr abort abs acos ' +
      'asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp ' +
      'fscanf isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper ' +
      'isxdigit tolower toupper labs ldexp log10 log malloc realloc memchr memcmp memcpy memset modf pow ' +
      'printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp ' +
      'strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan ' +
      'vfprintf vprintf vsprintf endl initializer_list unique_ptr',
    literal: 'true false nullptr NULL'
  };

  var EXPRESSION_CONTAINS = [
    CPP_PRIMITIVE_TYPES,
    hljs.C_LINE_COMMENT_MODE,
    hljs.C_BLOCK_COMMENT_MODE,
    NUMBERS,
    STRINGS
  ];

  return {
    aliases: ['c', 'cc', 'h', 'c++', 'h++', 'hpp'],
    keywords: CPP_KEYWORDS,
    illegal: '</',
    contains: EXPRESSION_CONTAINS.concat([
      PREPROCESSOR,
      {
        begin: '\\b(deque|list|queue|stack|vector|map|set|bitset|multiset|multimap|unordered_map|unordered_set|unordered_multiset|unordered_multimap|array)\\s*<', end: '>',
        keywords: CPP_KEYWORDS,
        contains: ['self', CPP_PRIMITIVE_TYPES]
      },
      {
        begin: hljs.IDENT_RE + '::',
        keywords: CPP_KEYWORDS
      },
      {
        // This mode covers expression context where we can't expect a function
        // definition and shouldn't highlight anything that looks like one:
        // `return some()`, `else if()`, `(x*sum(1, 2))`
        variants: [
          {begin: /=/, end: /;/},
          {begin: /\(/, end: /\)/},
          {beginKeywords: 'new throw return else', end: /;/}
        ],
        keywords: CPP_KEYWORDS,
        contains: EXPRESSION_CONTAINS.concat([
          {
            begin: /\(/, end: /\)/,
            keywords: CPP_KEYWORDS,
            contains: EXPRESSION_CONTAINS.concat(['self']),
            relevance: 0
          }
        ]),
        relevance: 0
      },
      {
        className: 'function',
        begin: '(' + hljs.IDENT_RE + '[\\*&\\s]+)+' + FUNCTION_TITLE,
        returnBegin: true, end: /[{;=]/,
        excludeEnd: true,
        keywords: CPP_KEYWORDS,
        illegal: /[^\w\s\*&]/,
        contains: [
          {
            begin: FUNCTION_TITLE, returnBegin: true,
            contains: [hljs.TITLE_MODE],
            relevance: 0
          },
          {
            className: 'params',
            begin: /\(/, end: /\)/,
            keywords: CPP_KEYWORDS,
            relevance: 0,
            contains: [
              hljs.C_LINE_COMMENT_MODE,
              hljs.C_BLOCK_COMMENT_MODE,
              STRINGS,
              NUMBERS,
              CPP_PRIMITIVE_TYPES
            ]
          },
          hljs.C_LINE_COMMENT_MODE,
          hljs.C_BLOCK_COMMENT_MODE,
          PREPROCESSOR
        ]
      }
    ]),
    exports: {
      preprocessor: PREPROCESSOR,
      strings: STRINGS,
      keywords: CPP_KEYWORDS
    }
  };
}
<|endoftext|>


<commit_before>/* heart.js v0.0.1
   copyright (c) 2012 darkf
   licensed under the terms of the MIT license, see LICENSE for details

   A Canvas-based graphics library inspired by (i.e. cloned from) Love 2D (https://love2d.org/)
   It's currently in its pre-alpha development stage, so don't expect anything to work,
   and feel free to send pull requests / file issues!

   Thank you for using heart.js! :-)
*/

(function (root, factory) {
	if (typeof define === 'function' && define.amd) {
		// AMD module
		define(factory);
	} else if (typeof exports === 'object') {
		// CommonJS module (Node or Browserify)
		module.exports = factory();
	} else {
		root.heart = factory();
	}
})(this, function() {
var heart = { _lastTick: new Date().getTime(), /* time of the last tick */
			  _dt: 0, /* time since last tick in seconds */
			  _fps: 0, /* frames per second */
			  _targetFPS: 30, /* the target FPS cap */
			  _bg: {r: 127, g: 127, b: 127}, /* background color */
			  _size: {w: 800, h: 600}, /* size of viewport */
			  _imagesLoading: [], /* for synchronous image loading */
			  _keysDown: {} /* which keys are down (char -> bool) */
			};

var HeartImage = function(img) {
	this.img = img;
};

HeartImage.prototype.getWidth = function() {
	return this.img.width;
};

HeartImage.prototype.getHeight = function() {
	return this.img.height;
};

heart.graphics = {
	rectangle: function(mode, x, y, w, h) {
		if(mode === "fill")
			heart.ctx.fillRect(x, y, w, h);
		else
			heart.ctx.strokeRect(x, y, w, h);
	},

	circle: function(mode, x, y, radius) {
		heart.ctx.beginPath();
		heart.ctx.arc(x, y, radius, 0, Math.PI*2, false);
		if(mode === "fill")
			heart.ctx.fill();
		else
			heart.ctx.stroke();
	},

	line: function(x1, y1, x2, y2) {
		heart.ctx.beginPath();
		heart.ctx.moveTo(x1, y1);
		heart.ctx.lineTo(x2, y2);
		heart.ctx.stroke();
	},

	print: function(text, x, y) {
		heart.ctx.fillText(text, x, y);
	},

	setColor: function(r, g, b, a) {
		if(a === undefined) {
			heart.ctx.fillStyle = heart.ctx.strokeStyle = "rgb("+r+","+g+","+b+")";
		}
		else {
			a = (a/255).toFixed(1); // input is in 0..255, output is in 0.0..1.0
			heart.ctx.fillStyle = heart.ctx.strokeStyle = "rgba("+r+","+g+","+b+","+a+")";
		}
	},

	getWidth: function() {
		return heart._size.w;
	},

	getHeight: function() {
		return heart._size.h;
	},

	getBackgroundColor: function() {
		return heart._bg;
	},

	setBackgroundColor: function(r, g, b) {
		heart._bg = {r: r, g: g, b: b};
	},

	newImage: function(src, callback) {
		/* load an image */
		/* XXX: does not handle errors */
		var img = new Image();
		heart._imagesLoading.push(img);
		img.onload = function() {
			heart._imagesLoading.splice(heart._imagesLoading.indexOf(img), 1); /* remove img from the loading sequence */
			callback(new HeartImage(img));
		};
		img.src = src;
	},

	draw: function(drawable, x, y) {
		if(drawable.img !== undefined) {
			heart.ctx.drawImage(drawable.img, x, y);
		}
	},

	translate: function(x, y) {
		heart.ctx.translate(x, y);
	},

	rotate: function(angle) {
		heart.ctx.rotate(angle);
	},

	push: function() {
		heart.ctx.save();
	},

	pop: function() {
		heart.ctx.restore();
	}
};

heart.timer = {
	getFPS: function() {
		return heart._fps;
	},

	getTargetFPS: function() {
		return heart._targetFPS;
	},

	setTargetFPS: function(fps) {
		heart._targetFPS = fps;
	},

	getTime: function() {
		return new Date().getTime();
	}
};

heart.keyboard = {
	isDown: function(key) {
		return heart._keysDown[key];
	},

	isUp: function(key) {
		return !heart.keyboard.isDown(key);
	}
};

heart.mouse = {
	_pos: {x: 0, y: 0},
	_btnState: {"l": false, "r": false}, /* left and right button press state */

	getPosition: function() {
		return [heart.mouse._pos.x, heart.mouse._pos.y];
	},

	getX: function() {
		return heart.mouse._pos.x;
	},

	getY: function() {
		return heart.mouse._pos.y;
	},

	isDown: function(button) {
		return heart.mouse._btnState[button] !== undefined ? heart.mouse._btnState[button] : false;
	}
};

heart._init = function() {
	/* if we're waiting on images to load, spinlock */
	if(heart._imagesLoading.length !== 0) {
		setTimeout(heart._init, 30 /* ms */);
		return;
	}

	if(heart.load !== undefined)
		heart.load();
	if(heart.canvas === undefined || heart.ctx === undefined)
		alert("no canvas");

	/* register for mouse-related events (pertaining to the canvas) */
	heart.canvas.onmousedown = function(e) {
		var btn = heart._mouseButtonName(e.which);
		heart.mouse._btnState[btn] = true;
		if(heart.mousepressed)
			heart.mousepressed(e.pageX, e.pageY, btn);
	};

	heart.canvas.onmouseup = function(e) {
		var btn = heart._mouseButtonName(e.which);
		heart.mouse._btnState[btn] = false;
		if(heart.mousereleased)
			heart.mousereleased(e.pageX, e.pageY, btn);
	};

	heart.canvas.onmousemove = function(e) {
		heart.mouse._pos = {x: e.pageX, y: e.pageY};
		if(heart.mousemoved)
			heart.mousemoved(e.pageX, e.pageY);
	};

	heart._tick(); /* first tick */
};

heart._tick = function() {
	var time = new Date().getTime();
	heart._dt = time - heart._lastTick;
	heart._lastTick = time;
	heart._fps = Math.floor(1000 / heart._dt);

	if(heart.update)
		heart.update(heart._dt / 1000);

	heart.ctx.fillStyle = "rgb("+heart._bg.r+","+heart._bg.g+","+heart._bg.b+")";
	heart.ctx.fillRect(0, 0, heart._size.w, heart._size.h);
	if(heart.draw)
		heart.draw();

	setTimeout(heart._tick, 1000 / heart._targetFPS);
};

heart.attach = function(canvas) {
	var el = document.getElementById(canvas);
	if(!el)
		return false;
	heart.canvas = el;
	heart.ctx = heart.canvas.getContext("2d");
	if(!heart.ctx)
		alert("couldn't get canvas context")
};

heart._mouseButtonName = function(n) {
	switch(n) {
		case 1: return "l";
		case 2: return "m";
		case 3: return "r";
	}

	return "unknown";
};

heart._getKeyChar = function(c) {
	/* supply a hacky keymap */
	switch(c) {
		/* arrow keys */
		case 38: return "up";
		case 37: return "left";
		case 39: return "right";
		case 40: return "down";
		case 27: return "escape";
		case 13: return "return";
	}

	return String.fromCharCode(c).toLowerCase();
};

// XXX: we need a keymap, since browsers decide on being annoying and
// not having a consistent keymap. (also, this won't work with special characters.)
window.onkeydown = function(e) {
	var c = heart._getKeyChar(e.keyCode);
	heart._keysDown[c] = true;
	if(heart.keydown !== undefined)
		heart.keydown(c);
};

window.onkeyup = function(e) {
	var c = heart._getKeyChar(e.keyCode);
	heart._keysDown[c] = false;
	if(heart.keyup !== undefined)
		heart.keyup(c);
};

window.onfocus = function(e) {
	if (heart.focus) heart.focus(true);
}
window.onblur = function(e) {
	if (heart.focus) heart.focus(false);
}

window.onload = function() {
	if(heart.preload !== undefined)
		heart.preload();
	heart._init();
};

return heart;
});
<commit_msg>move HeartImage inside heart<commit_after>/* heart.js v0.0.1
   copyright (c) 2012 darkf
   licensed under the terms of the MIT license, see LICENSE for details

   A Canvas-based graphics library inspired by (i.e. cloned from) Love 2D (https://love2d.org/)
   It's currently in its pre-alpha development stage, so don't expect anything to work,
   and feel free to send pull requests / file issues!

   Thank you for using heart.js! :-)
*/

(function (root, factory) {
	if (typeof define === 'function' && define.amd) {
		// AMD module
		define(factory);
	} else if (typeof exports === 'object') {
		// CommonJS module (Node or Browserify)
		module.exports = factory();
	} else {
		root.heart = factory();
	}
})(this, function() {
var heart = { _lastTick: new Date().getTime(), /* time of the last tick */
			  _dt: 0, /* time since last tick in seconds */
			  _fps: 0, /* frames per second */
			  _targetFPS: 30, /* the target FPS cap */
			  _bg: {r: 127, g: 127, b: 127}, /* background color */
			  _size: {w: 800, h: 600}, /* size of viewport */
			  _imagesLoading: [], /* for synchronous image loading */
			  _keysDown: {} /* which keys are down (char -> bool) */
			};

heart.HeartImage = function(img) {
	this.img = img;
};

heart.HeartImage.prototype.getWidth = function() {
	return this.img.width;
};

heart.HeartImage.prototype.getHeight = function() {
	return this.img.height;
};

heart.graphics = {
	rectangle: function(mode, x, y, w, h) {
		if(mode === "fill")
			heart.ctx.fillRect(x, y, w, h);
		else
			heart.ctx.strokeRect(x, y, w, h);
	},

	circle: function(mode, x, y, radius) {
		heart.ctx.beginPath();
		heart.ctx.arc(x, y, radius, 0, Math.PI*2, false);
		if(mode === "fill")
			heart.ctx.fill();
		else
			heart.ctx.stroke();
	},

	line: function(x1, y1, x2, y2) {
		heart.ctx.beginPath();
		heart.ctx.moveTo(x1, y1);
		heart.ctx.lineTo(x2, y2);
		heart.ctx.stroke();
	},

	print: function(text, x, y) {
		heart.ctx.fillText(text, x, y);
	},

	setColor: function(r, g, b, a) {
		if(a === undefined) {
			heart.ctx.fillStyle = heart.ctx.strokeStyle = "rgb("+r+","+g+","+b+")";
		}
		else {
			a = (a/255).toFixed(1); // input is in 0..255, output is in 0.0..1.0
			heart.ctx.fillStyle = heart.ctx.strokeStyle = "rgba("+r+","+g+","+b+","+a+")";
		}
	},

	getWidth: function() {
		return heart._size.w;
	},

	getHeight: function() {
		return heart._size.h;
	},

	getBackgroundColor: function() {
		return heart._bg;
	},

	setBackgroundColor: function(r, g, b) {
		heart._bg = {r: r, g: g, b: b};
	},

	newImage: function(src, callback) {
		/* load an image */
		/* XXX: does not handle errors */
		var img = new Image();
		heart._imagesLoading.push(img);
		img.onload = function() {
			heart._imagesLoading.splice(heart._imagesLoading.indexOf(img), 1); /* remove img from the loading sequence */
			callback(new heart.HeartImage(img));
		};
		img.src = src;
	},

	draw: function(drawable, x, y) {
		if(drawable.img !== undefined) {
			heart.ctx.drawImage(drawable.img, x, y);
		}
	},

	translate: function(x, y) {
		heart.ctx.translate(x, y);
	},

	rotate: function(angle) {
		heart.ctx.rotate(angle);
	},

	push: function() {
		heart.ctx.save();
	},

	pop: function() {
		heart.ctx.restore();
	}
};

heart.timer = {
	getFPS: function() {
		return heart._fps;
	},

	getTargetFPS: function() {
		return heart._targetFPS;
	},

	setTargetFPS: function(fps) {
		heart._targetFPS = fps;
	},

	getTime: function() {
		return new Date().getTime();
	}
};

heart.keyboard = {
	isDown: function(key) {
		return heart._keysDown[key];
	},

	isUp: function(key) {
		return !heart.keyboard.isDown(key);
	}
};

heart.mouse = {
	_pos: {x: 0, y: 0},
	_btnState: {"l": false, "r": false}, /* left and right button press state */

	getPosition: function() {
		return [heart.mouse._pos.x, heart.mouse._pos.y];
	},

	getX: function() {
		return heart.mouse._pos.x;
	},

	getY: function() {
		return heart.mouse._pos.y;
	},

	isDown: function(button) {
		return heart.mouse._btnState[button] !== undefined ? heart.mouse._btnState[button] : false;
	}
};

heart._init = function() {
	/* if we're waiting on images to load, spinlock */
	if(heart._imagesLoading.length !== 0) {
		setTimeout(heart._init, 30 /* ms */);
		return;
	}

	if(heart.load !== undefined)
		heart.load();
	if(heart.canvas === undefined || heart.ctx === undefined)
		alert("no canvas");

	/* register for mouse-related events (pertaining to the canvas) */
	heart.canvas.onmousedown = function(e) {
		var btn = heart._mouseButtonName(e.which);
		heart.mouse._btnState[btn] = true;
		if(heart.mousepressed)
			heart.mousepressed(e.pageX, e.pageY, btn);
	};

	heart.canvas.onmouseup = function(e) {
		var btn = heart._mouseButtonName(e.which);
		heart.mouse._btnState[btn] = false;
		if(heart.mousereleased)
			heart.mousereleased(e.pageX, e.pageY, btn);
	};

	heart.canvas.onmousemove = function(e) {
		heart.mouse._pos = {x: e.pageX, y: e.pageY};
		if(heart.mousemoved)
			heart.mousemoved(e.pageX, e.pageY);
	};

	heart._tick(); /* first tick */
};

heart._tick = function() {
	var time = new Date().getTime();
	heart._dt = time - heart._lastTick;
	heart._lastTick = time;
	heart._fps = Math.floor(1000 / heart._dt);

	if(heart.update)
		heart.update(heart._dt / 1000);

	heart.ctx.fillStyle = "rgb("+heart._bg.r+","+heart._bg.g+","+heart._bg.b+")";
	heart.ctx.fillRect(0, 0, heart._size.w, heart._size.h);
	if(heart.draw)
		heart.draw();

	setTimeout(heart._tick, 1000 / heart._targetFPS);
};

heart.attach = function(canvas) {
	var el = document.getElementById(canvas);
	if(!el)
		return false;
	heart.canvas = el;
	heart.ctx = heart.canvas.getContext("2d");
	if(!heart.ctx)
		alert("couldn't get canvas context")
};

heart._mouseButtonName = function(n) {
	switch(n) {
		case 1: return "l";
		case 2: return "m";
		case 3: return "r";
	}

	return "unknown";
};

heart._getKeyChar = function(c) {
	/* supply a hacky keymap */
	switch(c) {
		/* arrow keys */
		case 38: return "up";
		case 37: return "left";
		case 39: return "right";
		case 40: return "down";
		case 27: return "escape";
		case 13: return "return";
	}

	return String.fromCharCode(c).toLowerCase();
};

// XXX: we need a keymap, since browsers decide on being annoying and
// not having a consistent keymap. (also, this won't work with special characters.)
window.onkeydown = function(e) {
	var c = heart._getKeyChar(e.keyCode);
	heart._keysDown[c] = true;
	if(heart.keydown !== undefined)
		heart.keydown(c);
};

window.onkeyup = function(e) {
	var c = heart._getKeyChar(e.keyCode);
	heart._keysDown[c] = false;
	if(heart.keyup !== undefined)
		heart.keyup(c);
};

window.onfocus = function(e) {
	if (heart.focus) heart.focus(true);
}
window.onblur = function(e) {
	if (heart.focus) heart.focus(false);
}

window.onload = function() {
	if(heart.preload !== undefined)
		heart.preload();
	heart._init();
};

return heart;
});
<|endoftext|>