<commit_before>'use strict';
var _ = require('lodash');

//helper functions
function extractNames (v) {
  return v.name;
}

function get_get (roles,v) {
  return v.get(roles);
};

function remove_nulls (v) {
  return v;
}

//MenuItem class
function MenuItem (options) {
  options = _.assign ({name: null, title:null, link:null, roles:null}, options);
  if (!options.name) options.name = options.link;
  this.name = options.name;
  this.title = options.title;
  this.link = options.link;
  this.roles = options.roles;
  this.submenus = options.submenus || [];
}

function mapDoStrip (v) {
  return v ? v.strip() : undefined;
}

MenuItem.prototype.strip = function () {
  return {
    name: this.name,
    title:this.title,
    link: this.link,
    roles:this.roles,
    submenus: this.submenus.map(mapDoStrip)
  };
}

MenuItem.hasRole = function (role, roles) {
  return (roles.indexOf(role) > -1);
}

MenuItem.prototype.props = function () {
  return {
    name: this.name,
    title:this.title,
    link:this.link,
    roles:this.roles
  };
}

MenuItem.prototype.findOrCreate = function (path) {
  if (!path.length) return this;
  var p = path.shift();
  var index = this.list().indexOf(p);
  if (index > -1) return this.submenus[index].findOrCreate(path);
  var n = new MenuItem();
  n.name = p;
  this.submenus.push (n);
  return n.findOrCreate(path);
}

MenuItem.prototype.list = function () {
  return this.submenus.map(extractNames);
}

MenuItem.prototype.get = function (roles, path) {
  roles = roles ? roles.slice() : [];
  if (roles.indexOf('anonymous') < 0 && roles.indexOf('authenticated') < 0) {
    roles.push ('authenticated');
  }

  var list = this.list();
  if (path) {
    if (!path.length) return this;
    var n = path.shift();
    var index = list.indexOf (n);
    return this.submenus[index] ? this.submenus[index].get(roles,path) : undefined;
  }

  if(!MenuItem.hasRole('admin', roles)) {
    if ( this.roles) {
      if (!_.intersection(this.roles, roles).length) return undefined;
    }
  }

  return new MenuItem ({
    roles: this.roles || null,
    link : this.link || null,
    title:this.title || null,
    name : this.name || null,
    submenus : this.submenus.map(get_get.bind(null, roles)).filter(remove_nulls),
  });
}

MenuItem.prototype.add = function (mi) {
  var index = this.list().indexOf(mi.name);
  var itm;
  if (index > -1) {
    var ts = mi.props();
    itm = this.submenus[index];
    for (var i in ts) itm[i] = ts[i];
  }else{
    itm = mi;
    this.submenus.push (itm);
  }
  return itm;
}

var allMenus = new MenuItem (),
  _ = require('lodash');

function mapSubmenuNames (v) {
  return v.name;
}

function supportMenus(Meanio){
  function Menus() {
  };

  Menus.prototype.add = function(options) {
    if (options instanceof Array) {
      options.forEach( Menus.prototype.add.bind(this) );
      return this;
    };

    options = _.assign({
      path : 'main',
      roles: ['anonymous'],
    },
    options);
    options.path = options.path.replace(/^\//, '');
    var item = allMenus.findOrCreate(options.path.split('/'));
    item.add(new MenuItem(options));
    return this;
  };

  Menus.prototype.get = function(options) {
    options = options || {};
    options.menu = options.menu || 'main';
    options.roles = options.roles || ['anonymous'];
    options.defaultMenu = options.defaultMenu || [];

    var sm = allMenus.get(options.roles, options.menu.split('/'));
    if (!sm) {
      //no menu at all
      return options.defaultMenu;
    }
    var ret = sm.get(options.roles);
    return ret ? options.defaultMenu.concat(ret.submenus.map(mapDoStrip)) : options.defaultMenu;
/*

    var items = options.defaultMenu.concat(allMenus[options.menu] || []);
    items.forEach(function(item) {

      var hasRole = false;
      options.roles.forEach(function(role) {
        if (role === 'admin' || item.roles.indexOf('anonymous') !== -1 || item.roles.indexOf(role) !== -1) {
          hasRole = true;
        }
      });

      if (hasRole) {
        allowed.push(item);
      }
    });
    //console.log('getting menu for',options,'=>',allowed);
    return allowed;
    */
  };
  Meanio.prototype.Menus =  Menus;

};

module.exports = supportMenus;
<commit_msg>cleaning up jshint semi-colon issues<commit_after>'use strict';
var _ = require('lodash');

//helper functions
function extractNames (v) {
  return v.name;
}

function get_get (roles,v) {
  return v.get(roles);
}

function remove_nulls (v) {
  return v;
}

//MenuItem class
function MenuItem (options) {
  options = _.assign ({name: null, title:null, link:null, roles:null}, options);
  if (!options.name) options.name = options.link;
  this.name = options.name;
  this.title = options.title;
  this.link = options.link;
  this.roles = options.roles;
  this.submenus = options.submenus || [];
}

function mapDoStrip (v) {
  return v ? v.strip() : undefined;
}

MenuItem.prototype.strip = function () {
  return {
    name: this.name,
    title:this.title,
    link: this.link,
    roles:this.roles,
    submenus: this.submenus.map(mapDoStrip)
  };
};

MenuItem.hasRole = function (role, roles) {
  return (roles.indexOf(role) > -1);
};

MenuItem.prototype.props = function () {
  return {
    name: this.name,
    title:this.title,
    link:this.link,
    roles:this.roles
  };
};

MenuItem.prototype.findOrCreate = function (path) {
  if (!path.length) return this;
  var p = path.shift();
  var index = this.list().indexOf(p);
  if (index > -1) return this.submenus[index].findOrCreate(path);
  var n = new MenuItem();
  n.name = p;
  this.submenus.push (n);
  return n.findOrCreate(path);
};

MenuItem.prototype.list = function () {
  return this.submenus.map(extractNames);
};

MenuItem.prototype.get = function (roles, path) {
  roles = roles ? roles.slice() : [];
  if (roles.indexOf('anonymous') < 0 && roles.indexOf('authenticated') < 0) {
    roles.push ('authenticated');
  }

  var list = this.list();
  if (path) {
    if (!path.length) return this;
    var n = path.shift();
    var index = list.indexOf (n);
    return this.submenus[index] ? this.submenus[index].get(roles,path) : undefined;
  }

  if(!MenuItem.hasRole('admin', roles)) {
    if ( this.roles) {
      if (!_.intersection(this.roles, roles).length) return undefined;
    }
  }

  return new MenuItem ({
    roles: this.roles || null,
    link : this.link || null,
    title:this.title || null,
    name : this.name || null,
    submenus : this.submenus.map(get_get.bind(null, roles)).filter(remove_nulls),
  });
};

MenuItem.prototype.add = function (mi) {
  var index = this.list().indexOf(mi.name);
  var itm;
  if (index > -1) {
    var ts = mi.props();
    itm = this.submenus[index];
    for (var i in ts) itm[i] = ts[i];
  }else{
    itm = mi;
    this.submenus.push (itm);
  }
  return itm;
};

var allMenus = new MenuItem (),
  _ = require('lodash');

function mapSubmenuNames (v) {
  return v.name;
}

function supportMenus(Meanio){
  function Menus() {
  }

  Menus.prototype.add = function(options) {
    if (options instanceof Array) {
      options.forEach( Menus.prototype.add.bind(this) );
      return this;
    }

    options = _.assign({
      path : 'main',
      roles: ['anonymous'],
    },
    options);
    options.path = options.path.replace(/^\//, '');
    var item = allMenus.findOrCreate(options.path.split('/'));
    item.add(new MenuItem(options));
    return this;
  };

  Menus.prototype.get = function(options) {
    options = options || {};
    options.menu = options.menu || 'main';
    options.roles = options.roles || ['anonymous'];
    options.defaultMenu = options.defaultMenu || [];

    var sm = allMenus.get(options.roles, options.menu.split('/'));
    if (!sm) {
      //no menu at all
      return options.defaultMenu;
    }
    var ret = sm.get(options.roles);
    return ret ? options.defaultMenu.concat(ret.submenus.map(mapDoStrip)) : options.defaultMenu;
/*

    var items = options.defaultMenu.concat(allMenus[options.menu] || []);
    items.forEach(function(item) {

      var hasRole = false;
      options.roles.forEach(function(role) {
        if (role === 'admin' || item.roles.indexOf('anonymous') !== -1 || item.roles.indexOf(role) !== -1) {
          hasRole = true;
        }
      });

      if (hasRole) {
        allowed.push(item);
      }
    });
    //console.log('getting menu for',options,'=>',allowed);
    return allowed;
    */
  };
  Meanio.prototype.Menus =  Menus;

}

module.exports = supportMenus;
<|endoftext|>


<commit_before>/* eslint-env node */
/* eslint-disable no-console */

import fs from 'fs';
import path from 'path';

import dotenv from 'dotenv';
import {getSourcesWithArticles} from './newsapi';

dotenv.config();

const {MOCK_HEADLINES, NEWS_API_KEY} = process.env;

const HEADLINES_FILE = path.resolve(__dirname, '../site/headlines.json');
const MOCK_HEADLINES_FILE = path.resolve(
    __dirname,
    '../site/mock-headlines.json'
);

if (!NEWS_API_KEY) {
    if (MOCK_HEADLINES === '1') {
        fs.copyFileSync(MOCK_HEADLINES_FILE, HEADLINES_FILE);
        console.log('Using mock headlines');
    } else {
        console.error('The NEWS_API_KEY environment variable is not set.');
        console.error('Use template.env to Create a .env file.');
        console.error('You can also set MOCK_HEADLINES to 1 to use mock data.');
        process.exit(1);
    }
} else {
    getSourcesWithArticles(NEWS_API_KEY)
        .then(headlines => {
            fs.writeFileSync(HEADLINES_FILE, JSON.stringify(headlines));
            console.log('Retrieved real headlines');
        })
        .catch(error => {
            console.error(error);
            throw error;
        });
}
<commit_msg>Error out if fetching the headlines fails<commit_after>/* eslint-env node */
/* eslint-disable no-console */

import fs from 'fs';
import path from 'path';

import dotenv from 'dotenv';
import {getSourcesWithArticles} from './newsapi';

dotenv.config();

const {MOCK_HEADLINES, NEWS_API_KEY} = process.env;

const HEADLINES_FILE = path.resolve(__dirname, '../site/headlines.json');
const MOCK_HEADLINES_FILE = path.resolve(
    __dirname,
    '../site/mock-headlines.json'
);

if (!NEWS_API_KEY) {
    if (MOCK_HEADLINES === '1') {
        fs.copyFileSync(MOCK_HEADLINES_FILE, HEADLINES_FILE);
        console.log('Using mock headlines');
    } else {
        console.error('The NEWS_API_KEY environment variable is not set.');
        console.error('Use template.env to Create a .env file.');
        console.error('You can also set MOCK_HEADLINES to 1 to use mock data.');
        process.exit(1);
    }
} else {
    getSourcesWithArticles(NEWS_API_KEY)
        .then(headlines => {
            fs.writeFileSync(HEADLINES_FILE, JSON.stringify(headlines));
            console.log('Retrieved real headlines');
        })
        .catch(error => {
            console.error(error);
            process.exit(1);
        });
}
<|endoftext|>


<commit_before>var CourseModal = {
    show_course_modal: function(term, course_index) {
        var source   = $("#course_modal").html();
        var template = Handlebars.compile(source);

        var course_data;
        if (term) {
            WSData.normalize_instructors_for_term(term);
            course_data = WSData.course_data_for_term(term);
        }
        else {
            WSData.normalize_instructors();
            course_data = WSData.current_course_data(term);
        }
        var section = course_data.sections[course_index];

        if (section.class_website_url || section.canvas_url) {
            section.has_resources = true;
        }

        var content = template(section);
        Modal.html(template(section));

        Modal.show();

        $('html,body').animate({scrollTop: 0}, 'fast');

        $(".instructor").bind("click", function(ev) {
            var hist = window.History;
            hist.pushState({
                state: "instructor",
                instructor: ev.target.rel
            },  "", "/mobile/instructor/"+ev.target.rel);

            return false;
        });

        $(".close_modal").on("click", function() {
            History.replaceState({
                state: "visual"
            },  "", "/mobile/visual");
        });

        $(".course_website").on("click", function(ev) {
            var course_id = ev.currentTarget.getAttribute("rel");
            course_id = course_id.replace(/[^a-z0-9]/gi, '_');
            WSData.log_interaction("open_course_website_"+course_id);
        });

        $(".show_map_modal").on("click", function(ev) {
            var building = ev.currentTarget.getAttribute("rel");
            building = building.replace(/[^a-z0-9]/gi, '_');
            WSData.log_interaction("show_map_from_course_modal_"+building);
        });

    }
};

<commit_msg>Makes closing a modal for a future quarter show the correct future quarter<commit_after>var CourseModal = {
    show_course_modal: function(term, course_index) {
        var source   = $("#course_modal").html();
        var template = Handlebars.compile(source);

        var course_data;
        if (term) {
            WSData.normalize_instructors_for_term(term);
            course_data = WSData.course_data_for_term(term);
        }
        else {
            WSData.normalize_instructors();
            course_data = WSData.current_course_data(term);
        }
        var section = course_data.sections[course_index];

        if (section.class_website_url || section.canvas_url) {
            section.has_resources = true;
        }

        var content = template(section);
        Modal.html(template(section));

        Modal.show();

        $('html,body').animate({scrollTop: 0}, 'fast');

        $(".instructor").bind("click", function(ev) {
            var hist = window.History;
            hist.pushState({
                state: "instructor",
                instructor: ev.target.rel
            },  "", "/mobile/instructor/"+ev.target.rel);

            return false;
        });

        var state = { state: "visual" };
        var url = "/mobile/visual";

        if (term) {
            state.term = term;
            url += "/"+term;
        }
        $(".close_modal").on("click", function() {
            History.replaceState(state, "", url);
        });

        $(".course_website").on("click", function(ev) {
            var course_id = ev.currentTarget.getAttribute("rel");
            course_id = course_id.replace(/[^a-z0-9]/gi, '_');
            WSData.log_interaction("open_course_website_"+course_id);
        });

        $(".show_map_modal").on("click", function(ev) {
            var building = ev.currentTarget.getAttribute("rel");
            building = building.replace(/[^a-z0-9]/gi, '_');
            WSData.log_interaction("show_map_from_course_modal_"+building);
        });

    }
};

<|endoftext|>


<commit_before>// Load modules

var Fs = require('fs');
var Hoek = require('hoek');
var MimeDb = require('mime-db');
var Path = require('path');

// Declare internals
var internals = {};
internals.types = {};

var keys = Object.keys(MimeDb);

// Create an internal object to retrieve a mime type based on the extension
for (var i = 0, il = keys.length; i < il; i++) {
    var name = keys[i];
    var mime = MimeDb[name];
    mime.name = name;
    var extensions = mime.extensions || [];

    for (var j = 0, jl = extensions.length; j < jl; j++) {

        var ext = extensions[j];
        internals.types[ext] = mime;
    }
}

exports.fromPath = function (path) {

    if (typeof path !== 'string') {
        return false;
    }

    var extension = Path.extname(path).slice(1);

    return internals.types[extension] || false;
};
<commit_msg>PR changes.<commit_after>// Load modules

var MimeDb = require('mime-db');
var Path = require('path');

// Declare internals
var internals = {};
internals.types = {};

internals.compile = function () {

    var map = {};
    var keys = Object.keys(MimeDb);

    // Create an internal object to retrieve a mime type based on the extension
    for (var i = 0, il = keys.length; i < il; i++) {
        var name = keys[i];
        var mime = MimeDb[name];
        mime.name = name;
        var extensions = mime.extensions || [];

        for (var j = 0, jl = extensions.length; j < jl; j++) {

            var ext = extensions[j];
            map[ext] = mime;
        }
    }

    return map;
};

internals.types = internals.compile();

exports.fromPath = function (path) {

    if (typeof path !== 'string') {
        return false;
    }

    var extension = Path.extname(path).slice(1);

    return internals.types[extension] || false;
};
<|endoftext|>


<commit_before>define([
	'base/js/namespace',
	'base/js/events',
	'notebook/js/codecell'
], function (
	Jupyter,
	events,
	codecell
){
	'use strict';

	var CodeCell = codecell.CodeCell;

	function patch_CodeCell_execute () {
		console.log('[Freeze] patching CodeCell.prototype.execute')
		var old_execute = CodeCell.prototype.execute;

		CodeCell.prototype.execute = function () {
			if (!this.metadata.run_control.frozen) {
				old_execute.apply(this, arguments);
			}
		}
	}

	function set_state(cell, state) {
		if (cell instanceof CodeCell) {
			if (cell.metadata.run_control == undefined)
				cell.metadata.run_control = {};
			if (state == undefined)
				state = 'normal';
			switch(state) {
				case 'normal':
					cell.metadata.run_control.frozen = false;
					cell.metadata.run_control.read_only = false;
					var bg = "#f5f5f5";
					break;
				case 'read_only':
					cell.metadata.run_control.frozen = false;
					cell.metadata.run_control.read_only = true;
					var bg = "#FFFEF0";
					break;
				case 'frozen':
					cell.metadata.run_control.frozen = true;
					cell.metadata.run_control.read_only = true;
					var bg = "#f0feff";
					break;
			}
			cell.code_mirror.setOption('readOnly', cell.metadata.run_control.read_only);
			var prompt = cell.element.find('div.input_area');
			prompt.css("background-color", bg);
		}
	}

	function set_state_selected (state) {
		var cells = Jupyter.notebook.get_selected_cells();
		for (var i = 0; i < cells.length; i++) {
			set_state(cells[i], state)
		}
	}

	function make_normal_selected () {
		set_state_selected('normal');
	}

	function make_read_only_selected () {
		set_state_selected('read_only');
	}

	function make_frozen_selected () {
		set_state_selected('frozen');
	}

	function init () {
		var cells = Jupyter.notebook.get_cells();
		for (var i in cells) {
			var cell = cells[i];
			if (cell instanceof CodeCell) {
				if (cell.metadata.run_control != undefined) {
					if (cell.metadata.run_control.read_only) {
						if (cell.metadata.run_control.frozen) {
							set_state(cell, 'frozen')
						} else {
							set_state(cell, 'read_only')
						}
					} else {
						set_state(cell, 'normal')
					}
				} else {
					set_state(cell, 'normal');
				}
			}
		}
	}

	Jupyter.toolbar.add_buttons_group([
		{
			id : 'make_normal',
			label : 'lift restrictions from selected cells',
			icon : 'fa-unlock-alt',
			callback : make_normal_selected
		},
		{
			id : 'make_read_only',
			label : 'make selected cells read-only',
			icon: 'fa-lock',
			callback : make_read_only_selected

		},
		{
			id : 'freeze_cells',
			label : 'freeze selected cells',
			icon : 'fa-asterisk',
			callback : make_frozen_selected
		}
	]);

	init();

	patch_CodeCell_execute();
});
<commit_msg>Add `load_extension` function and `return` value<commit_after>define([
	'base/js/namespace',
	'base/js/events',
	'notebook/js/codecell'
], function (
	Jupyter,
	events,
	codecell
){
	'use strict';

	var CodeCell = codecell.CodeCell;

	function patch_CodeCell_execute () {
		console.log('[Freeze] patching CodeCell.prototype.execute')
		var old_execute = CodeCell.prototype.execute;

		CodeCell.prototype.execute = function () {
			if (!this.metadata.run_control.frozen) {
				old_execute.apply(this, arguments);
			}
		}
	}

	function set_state(cell, state) {
		if (cell instanceof CodeCell) {
			if (cell.metadata.run_control == undefined)
				cell.metadata.run_control = {};
			if (state == undefined)
				state = 'normal';
			switch(state) {
				case 'normal':
					cell.metadata.run_control.frozen = false;
					cell.metadata.run_control.read_only = false;
					var bg = "#f5f5f5";
					break;
				case 'read_only':
					cell.metadata.run_control.frozen = false;
					cell.metadata.run_control.read_only = true;
					var bg = "#FFFEF0";
					break;
				case 'frozen':
					cell.metadata.run_control.frozen = true;
					cell.metadata.run_control.read_only = true;
					var bg = "#f0feff";
					break;
			}
			cell.code_mirror.setOption('readOnly', cell.metadata.run_control.read_only);
			var prompt = cell.element.find('div.input_area');
			prompt.css("background-color", bg);
		}
	}

	function set_state_selected (state) {
		var cells = Jupyter.notebook.get_selected_cells();
		for (var i = 0; i < cells.length; i++) {
			set_state(cells[i], state)
		}
	}

	function make_normal_selected () {
		set_state_selected('normal');
	}

	function make_read_only_selected () {
		set_state_selected('read_only');
	}

	function make_frozen_selected () {
		set_state_selected('frozen');
	}

	function initialize_states () {
		var cells = Jupyter.notebook.get_cells();
		for (var i in cells) {
			var cell = cells[i];
			if (cell instanceof CodeCell) {
				if (cell.metadata.run_control != undefined) {
					if (cell.metadata.run_control.read_only) {
						if (cell.metadata.run_control.frozen) {
							set_state(cell, 'frozen')
						} else {
							set_state(cell, 'read_only')
						}
					} else {
						set_state(cell, 'normal')
					}
				} else {
					set_state(cell, 'normal');
				}
			}
		}
	}

	function load_extension () {
		Jupyter.toolbar.add_buttons_group([
			{
				id : 'make_normal',
				label : 'lift restrictions from selected cells',
				icon : 'fa-unlock-alt',
				callback : make_normal_selected
			},
			{
				id : 'make_read_only',
				label : 'make selected cells read-only',
				icon: 'fa-lock',
				callback : make_read_only_selected
			},
			{
				id : 'freeze_cells',
				label : 'freeze selected cells',
				icon : 'fa-asterisk',
				callback : make_frozen_selected
			}
		]);

		initialize_states();

		patch_CodeCell_execute();
	}

	return {
		load_jupyter_extension : load_extension,
		load_ipython_extension : load_extension
	}
});
<|endoftext|>


<commit_before>/* global GLProgram */
// Thin GL program wrapp to cache uniform locations/values, do compile-time pre-processing
// (injecting #defines and #pragma transforms into shaders), etc.
import {GL} from './gl';
import GLTexture from './gl_texture';

GLProgram.id = 0; // assign each program a unique id
GLProgram.programs = {}; // programs, by id

export default function GLProgram (gl, vertex_shader, fragment_shader, options)
{
    options = options || {};

    this.gl = gl;
    this.program = null;
    this.compiled = false;
    this.compiling = false;
    this.defines = Object.assign({}, options.defines||{}); // key/values inserted as #defines into shaders at compile-time
    this.transforms = Object.assign({}, options.transforms||{}); // key/values for URLs of blocks that can be injected into shaders at compile-time
    this.uniforms = {}; // program locations of uniforms, set/updated at compile-time
    this.attribs = {}; // program locations of vertex attributes

    this.vertex_shader = vertex_shader;
    this.fragment_shader = fragment_shader;

    this.id = GLProgram.id++;
    GLProgram.programs[this.id] = this;
    this.name = options.name; // can provide a program name (useful for debugging)

    this.compile();
}

GLProgram.prototype.destroy = function () {
    this.gl.useProgram(null);
    this.gl.deleteProgram(this.program);
    this.program = null;
    this.uniforms = {};
    this.attribs = {};
    delete GLProgram.programs[this.id];
    this.compiled = false;
};

// Use program wrapper with simple state cache
GLProgram.prototype.use = function ()
{
    if (!this.compiled) {
        return;
    }

    if (GLProgram.current !== this) {
        this.gl.useProgram(this.program);
    }
    GLProgram.current = this;
};
GLProgram.current = null;

// Global config applied to all programs (duplicate properties for a specific program will take precedence)
GLProgram.defines = {};
GLProgram.transforms = {};

GLProgram.addTransform = function (key, ...transforms) {
    GLProgram.transforms[key] = GLProgram.transforms[key] || [];
    GLProgram.transforms[key].push(...transforms);
};

// Remove all global shader transforms for a given key
GLProgram.removeTransform = function (key) {
    GLProgram.transforms[key] = [];
};

GLProgram.prototype.compile = function () {

    if (this.compiling) {
        throw(new Error(`GLProgram.compile(): skipping for ${this.id} (${this.name}) because already compiling`));
    }
    this.compiling = true;
    this.compiled = false;

    // Copy sources from pre-modified template
    this.computed_vertex_shader = this.vertex_shader;
    this.computed_fragment_shader = this.fragment_shader;

    // Make list of defines to be injected later
    var defines = this.buildDefineList();

    // Inject user-defined transforms (arbitrary code points matching named #pragmas)
    // Replace according to this pattern:
    // #pragma tangram: [key]
    // e.g. #pragma tangram: globals

    // TODO: support glslify #pragma export names for better compatibility? (e.g. rename main() functions)
    // TODO: auto-insert uniforms referenced in mode definition, but not in shader base or transforms? (problem: don't have access to uniform list/type here)

    // Gather all transform code snippets
    var transforms = this.buildShaderTransformList();
    var regexp;

    for (var key in transforms) {
        var transform = transforms[key];
        if (!transform) {
            continue;
        }

        // First find code replace points in shaders
        regexp = new RegExp('^\\s*#pragma\\s+tangram:\\s+' + key + '\\s*$', 'm');
        var inject_vertex = this.computed_vertex_shader.match(regexp);
        var inject_fragment = this.computed_fragment_shader.match(regexp);

        // Avoid network request if nothing to replace
        if (inject_vertex == null && inject_fragment == null) {
            continue;
        }

        // Each key can be a single string or array of strings
        var source = transform;
        if (Array.isArray(transform)) {
            // Combine all transforms into one string
            source = transform.reduce((prev, cur) => `${prev}\n${cur}`);
        }

        // Inject
        if (inject_vertex != null) {
            this.computed_vertex_shader = this.computed_vertex_shader.replace(regexp, source);
        }
        if (inject_fragment != null) {
            this.computed_fragment_shader = this.computed_fragment_shader.replace(regexp, source);
        }

        // Add a #define for this injection point
        defines['TANGRAM_TRANSFORM_' + key.replace(' ', '_').toUpperCase()] = true;
    }

    // Clean-up any #pragmas that weren't replaced (to prevent compiler warnings)
    regexp = new RegExp('^\\s*#pragma\\s+tangram:\\s+\\w+\\s*$', 'gm');
    this.computed_vertex_shader = this.computed_vertex_shader.replace(regexp, '');
    this.computed_fragment_shader = this.computed_fragment_shader.replace(regexp, '');

    // Build & inject defines
    // This is done *after* code injection so that we can add defines for which code points were injected
    var define_str = GLProgram.buildDefineString(defines);
    this.computed_vertex_shader = define_str + this.computed_vertex_shader;
    this.computed_fragment_shader = define_str + this.computed_fragment_shader;

    // Include program info useful for debugging
    var info = (this.name ? (this.name + ' / id ' + this.id) : ('id ' + this.id));
    this.computed_vertex_shader = '// Program: ' + info + '\n' + this.computed_vertex_shader;
    this.computed_fragment_shader = '// Program: ' + info + '\n' + this.computed_fragment_shader;

    // Compile & set uniforms to cached values
    try {
        this.program = GL.updateProgram(this.gl, this.program, this.computed_vertex_shader, this.computed_fragment_shader);
        this.compiled = true;
        this.compiling = false;
    }
    catch(error) {
        this.program = null;
        this.compiled = false;
        this.compiling = false;
        throw(new Error(`GLProgram.compile(): program ${this.id} (${this.name}) error:`, error));
    }

    this.use();
    this.refreshUniforms();
    this.refreshAttributes();
};

// Make list of defines (global, then program-specific)
GLProgram.prototype.buildDefineList = function () {
    var d, defines = {};
    for (d in GLProgram.defines) {
        defines[d] = GLProgram.defines[d];
    }
    for (d in this.defines) {
        defines[d] = this.defines[d];
    }
    return defines;
};

// Make list of shader transforms (global, then program-specific)
GLProgram.prototype.buildShaderTransformList = function () {
    var d, transforms = {};
    for (d in GLProgram.transforms) {
        transforms[d] = [];

        if (Array.isArray(GLProgram.transforms[d])) {
            transforms[d].push(...GLProgram.transforms[d]);
        }
        else {
            transforms[d] = [GLProgram.transforms[d]];
        }
    }
    for (d in this.transforms) {
        transforms[d] = transforms[d] || [];

        if (Array.isArray(this.transforms[d])) {
            transforms[d].push(...this.transforms[d]);
        }
        else {
            transforms[d].push(this.transforms[d]);
        }
    }
    return transforms;
};

// Turn #defines into a combined string
GLProgram.buildDefineString = function (defines) {
    var define_str = "";
    for (var d in defines) {
        if (defines[d] === false) {
            continue;
        }
        else if (typeof defines[d] === 'boolean' && defines[d] === true) { // booleans are simple defines with no value
            define_str += "#define " + d + "\n";
        }
        else if (typeof defines[d] === 'number' && Math.floor(defines[d]) === defines[d]) { // int to float conversion to satisfy GLSL floats
            define_str += "#define " + d + " " + defines[d].toFixed(1) + "\n";
        }
        else { // any other float or string value
            define_str += "#define " + d + " " + defines[d] + "\n";
        }
    }
    return define_str;
};

// Set uniforms from a JS object, with inferred types
GLProgram.prototype.setUniforms = function (uniforms, prefix, texture_unit = 0)
{
    if (!this.compiled) {
        return;
    }

    // TODO: only update uniforms when changed

    // Track active texture unit
    // A previous value will be passed in when setting GLSL structures, which call setUniforms recursively
    this.texture_unit = texture_unit;

    for (var name in uniforms) {
        var uniform = uniforms[name];
        var u;

        if (prefix) {
            name = prefix + '.' + name;
        }

        // Single float
        if (typeof uniform === 'number') {
            this.uniform('1f', name, uniform);
        }
        // Array: vector, array of floats, array of textures, or array of structs
        else if (Array.isArray(uniform)) {
            // Numeric values
            if (typeof uniform[0] === 'number') {
                // float vectors (vec2, vec3, vec4)
                if (uniform.length >= 2 && uniform.length <= 4) {
                    this.uniform(uniform.length + 'fv', name, uniform);
                }
                // float array
                else if (uniform.length > 4) {
                    this.uniform('1fv', name + '[0]', uniform);
                }
                // TODO: assume matrix for (typeof == Float32Array && length == 16)?
            }
            // Array of textures
            else if (typeof uniform[0] === 'string') {
                for (u=0; u < uniform.length; u++) {
                    this.setTextureUniform(name + '[' + u + ']', uniform[u]);
                }
            }
            // Array of arrays - but only arrays of vectors are allowed in this case
            else if (Array.isArray(uniform[0]) && typeof uniform[0][0] === 'number') {
                // float vectors (vec2, vec3, vec4)
                if (uniform[0].length >= 2 && uniform[0].length <= 4) {
                    // Set each vector in the array
                    for (u=0; u < uniform.length; u++) {
                        this.uniform(uniform[u].length + 'fv', name + '[' + u + ']', uniform[u]);
                    }
                }
                // else error?
            }
            // Array of structures
            else if (typeof uniform[0] === 'object') {
                for (u=0; u < uniform.length; u++) {
                    // For each element in the struct array, set each field, passing along current texture unit
                    this.setUniforms(uniform[u], name + '[' + u + ']', this.texture_unit);
                }
            }
        }
        // Boolean
        else if (typeof uniform === 'boolean') {
            this.uniform('1i', name, uniform);
        }
        // Texture
        else if (typeof uniform === 'string') {
            this.setTextureUniform(name, uniform);
        }
        // Structure
        else if (typeof uniform === 'object') {
            // Set each field in the struct, passing along current texture unit
            this.setUniforms(uniform, name, this.texture_unit);
        }

        // TODO: support other non-float types? (int, etc.)
    }
};

// Set a texture uniform, finds texture by name or creates a new one
GLProgram.prototype.setTextureUniform = function (uniform_name, texture_name) {
    var texture = GLTexture.textures[texture_name];
    if (texture == null) {
        texture = new GLTexture(this.gl, texture_name);
        texture.load(texture_name);
    }

    texture.bind(this.texture_unit);
    this.uniform('1i', uniform_name, this.texture_unit);
    this.texture_unit++; // TODO: track max texture units and log/throw errors
};

// ex: program.uniform('3f', 'position', x, y, z);
// TODO: only update uniforms when changed
GLProgram.prototype.uniform = function (method, name, ...values) // 'values' is a method-appropriate arguments list
{
    if (!this.compiled) {
        return;
    }

    var uniform = (this.uniforms[name] = this.uniforms[name] || {});
    uniform.name = name;
    uniform.location = uniform.location || this.gl.getUniformLocation(this.program, name);
    uniform.method = 'uniform' + method;
    uniform.values = values;
    this.updateUniform(name);
};

// Set a single uniform
GLProgram.prototype.updateUniform = function (name)
{
    if (!this.compiled) {
        return;
    }

    var uniform = this.uniforms[name];
    if (uniform == null || uniform.location == null) {
        return;
    }

    this.use();
    this.gl[uniform.method].apply(this.gl, [uniform.location].concat(uniform.values)); // call appropriate GL uniform method and pass through arguments
};

// Refresh uniform locations and set to last cached values
GLProgram.prototype.refreshUniforms = function ()
{
    if (!this.compiled) {
        return;
    }

    for (var u in this.uniforms) {
        this.uniforms[u].location = this.gl.getUniformLocation(this.program, u);
        this.updateUniform(u);
    }
};

GLProgram.prototype.refreshAttributes = function ()
{
    // var len = this.gl.getProgramParameter(this.program, this.gl.ACTIVE_ATTRIBUTES);
    // for (var i=0; i < len; i++) {
    //     var a = this.gl.getActiveAttrib(this.program, i);
    // }
    this.attribs = {};
};

// Get the location of a vertex attribute
GLProgram.prototype.attribute = function (name)
{
    if (!this.compiled) {
        return;
    }

    var attrib = (this.attribs[name] = this.attribs[name] || {});
    if (attrib.location != null) {
        return attrib;
    }

    attrib.name = name;
    attrib.location = this.gl.getAttribLocation(this.program, name);

    // var info = this.gl.getActiveAttrib(this.program, attrib.location);
    // attrib.type = info.type;
    // attrib.size = info.size;

    return attrib;
};
<commit_msg>uniforms: check for a uniform definition in a GLSL source string (currently justs uses a regular expression, so this is brittle)<commit_after>/* global GLProgram */
// Thin GL program wrapp to cache uniform locations/values, do compile-time pre-processing
// (injecting #defines and #pragma transforms into shaders), etc.
import {GL} from './gl';
import GLTexture from './gl_texture';

GLProgram.id = 0; // assign each program a unique id
GLProgram.programs = {}; // programs, by id

export default function GLProgram (gl, vertex_shader, fragment_shader, options)
{
    options = options || {};

    this.gl = gl;
    this.program = null;
    this.compiled = false;
    this.compiling = false;
    this.defines = Object.assign({}, options.defines||{}); // key/values inserted as #defines into shaders at compile-time
    this.transforms = Object.assign({}, options.transforms||{}); // key/values for URLs of blocks that can be injected into shaders at compile-time
    this.uniforms = {}; // program locations of uniforms, set/updated at compile-time
    this.attribs = {}; // program locations of vertex attributes

    this.vertex_shader = vertex_shader;
    this.fragment_shader = fragment_shader;

    this.id = GLProgram.id++;
    GLProgram.programs[this.id] = this;
    this.name = options.name; // can provide a program name (useful for debugging)

    this.compile();
}

GLProgram.prototype.destroy = function () {
    this.gl.useProgram(null);
    this.gl.deleteProgram(this.program);
    this.program = null;
    this.uniforms = {};
    this.attribs = {};
    delete GLProgram.programs[this.id];
    this.compiled = false;
};

// Use program wrapper with simple state cache
GLProgram.prototype.use = function ()
{
    if (!this.compiled) {
        return;
    }

    if (GLProgram.current !== this) {
        this.gl.useProgram(this.program);
    }
    GLProgram.current = this;
};
GLProgram.current = null;

// Global config applied to all programs (duplicate properties for a specific program will take precedence)
GLProgram.defines = {};
GLProgram.transforms = {};

GLProgram.addTransform = function (key, ...transforms) {
    GLProgram.transforms[key] = GLProgram.transforms[key] || [];
    GLProgram.transforms[key].push(...transforms);
};

// Remove all global shader transforms for a given key
GLProgram.removeTransform = function (key) {
    GLProgram.transforms[key] = [];
};

GLProgram.prototype.compile = function () {

    if (this.compiling) {
        throw(new Error(`GLProgram.compile(): skipping for ${this.id} (${this.name}) because already compiling`));
    }
    this.compiling = true;
    this.compiled = false;

    // Copy sources from pre-modified template
    this.computed_vertex_shader = this.vertex_shader;
    this.computed_fragment_shader = this.fragment_shader;

    // Make list of defines to be injected later
    var defines = this.buildDefineList();

    // Inject user-defined transforms (arbitrary code points matching named #pragmas)
    // Replace according to this pattern:
    // #pragma tangram: [key]
    // e.g. #pragma tangram: globals

    // TODO: support glslify #pragma export names for better compatibility? (e.g. rename main() functions)
    // TODO: auto-insert uniforms referenced in mode definition, but not in shader base or transforms? (problem: don't have access to uniform list/type here)

    // Gather all transform code snippets
    var transforms = this.buildShaderTransformList();
    var regexp;

    for (var key in transforms) {
        var transform = transforms[key];
        if (!transform) {
            continue;
        }

        // First find code replace points in shaders
        regexp = new RegExp('^\\s*#pragma\\s+tangram:\\s+' + key + '\\s*$', 'm');
        var inject_vertex = this.computed_vertex_shader.match(regexp);
        var inject_fragment = this.computed_fragment_shader.match(regexp);

        // Avoid network request if nothing to replace
        if (inject_vertex == null && inject_fragment == null) {
            continue;
        }

        // Each key can be a single string or array of strings
        var source = transform;
        if (Array.isArray(transform)) {
            // Combine all transforms into one string
            source = transform.reduce((prev, cur) => `${prev}\n${cur}`);
        }

        // Inject
        if (inject_vertex != null) {
            this.computed_vertex_shader = this.computed_vertex_shader.replace(regexp, source);
        }
        if (inject_fragment != null) {
            this.computed_fragment_shader = this.computed_fragment_shader.replace(regexp, source);
        }

        // Add a #define for this injection point
        defines['TANGRAM_TRANSFORM_' + key.replace(' ', '_').toUpperCase()] = true;
    }

    // Clean-up any #pragmas that weren't replaced (to prevent compiler warnings)
    regexp = new RegExp('^\\s*#pragma\\s+tangram:\\s+\\w+\\s*$', 'gm');
    this.computed_vertex_shader = this.computed_vertex_shader.replace(regexp, '');
    this.computed_fragment_shader = this.computed_fragment_shader.replace(regexp, '');

    // Build & inject defines
    // This is done *after* code injection so that we can add defines for which code points were injected
    var define_str = GLProgram.buildDefineString(defines);
    this.computed_vertex_shader = define_str + this.computed_vertex_shader;
    this.computed_fragment_shader = define_str + this.computed_fragment_shader;

    // Include program info useful for debugging
    var info = (this.name ? (this.name + ' / id ' + this.id) : ('id ' + this.id));
    this.computed_vertex_shader = '// Program: ' + info + '\n' + this.computed_vertex_shader;
    this.computed_fragment_shader = '// Program: ' + info + '\n' + this.computed_fragment_shader;

    // Compile & set uniforms to cached values
    try {
        this.program = GL.updateProgram(this.gl, this.program, this.computed_vertex_shader, this.computed_fragment_shader);
        this.compiled = true;
        this.compiling = false;
    }
    catch(error) {
        this.program = null;
        this.compiled = false;
        this.compiling = false;
        throw(new Error(`GLProgram.compile(): program ${this.id} (${this.name}) error:`, error));
    }

    this.use();
    this.refreshUniforms();
    this.refreshAttributes();
};

// Make list of defines (global, then program-specific)
GLProgram.prototype.buildDefineList = function () {
    var d, defines = {};
    for (d in GLProgram.defines) {
        defines[d] = GLProgram.defines[d];
    }
    for (d in this.defines) {
        defines[d] = this.defines[d];
    }
    return defines;
};

// Make list of shader transforms (global, then program-specific)
GLProgram.prototype.buildShaderTransformList = function () {
    var d, transforms = {};
    for (d in GLProgram.transforms) {
        transforms[d] = [];

        if (Array.isArray(GLProgram.transforms[d])) {
            transforms[d].push(...GLProgram.transforms[d]);
        }
        else {
            transforms[d] = [GLProgram.transforms[d]];
        }
    }
    for (d in this.transforms) {
        transforms[d] = transforms[d] || [];

        if (Array.isArray(this.transforms[d])) {
            transforms[d].push(...this.transforms[d]);
        }
        else {
            transforms[d].push(this.transforms[d]);
        }
    }
    return transforms;
};

// Turn #defines into a combined string
GLProgram.buildDefineString = function (defines) {
    var define_str = "";
    for (var d in defines) {
        if (defines[d] === false) {
            continue;
        }
        else if (typeof defines[d] === 'boolean' && defines[d] === true) { // booleans are simple defines with no value
            define_str += "#define " + d + "\n";
        }
        else if (typeof defines[d] === 'number' && Math.floor(defines[d]) === defines[d]) { // int to float conversion to satisfy GLSL floats
            define_str += "#define " + d + " " + defines[d].toFixed(1) + "\n";
        }
        else { // any other float or string value
            define_str += "#define " + d + " " + defines[d] + "\n";
        }
    }
    return define_str;
};

// Set uniforms from a JS object, with inferred types
GLProgram.prototype.setUniforms = function (uniforms, prefix, texture_unit = 0)
{
    if (!this.compiled) {
        return;
    }

    // TODO: only update uniforms when changed

    // Track active texture unit
    // A previous value will be passed in when setting GLSL structures, which call setUniforms recursively
    this.texture_unit = texture_unit;

    for (var name in uniforms) {
        var uniform = uniforms[name];
        var u;

        if (prefix) {
            name = prefix + '.' + name;
        }

        // Single float
        if (typeof uniform === 'number') {
            this.uniform('1f', name, uniform);
        }
        // Array: vector, array of floats, array of textures, or array of structs
        else if (Array.isArray(uniform)) {
            // Numeric values
            if (typeof uniform[0] === 'number') {
                // float vectors (vec2, vec3, vec4)
                if (uniform.length >= 2 && uniform.length <= 4) {
                    this.uniform(uniform.length + 'fv', name, uniform);
                }
                // float array
                else if (uniform.length > 4) {
                    this.uniform('1fv', name + '[0]', uniform);
                }
                // TODO: assume matrix for (typeof == Float32Array && length == 16)?
            }
            // Array of textures
            else if (typeof uniform[0] === 'string') {
                for (u=0; u < uniform.length; u++) {
                    this.setTextureUniform(name + '[' + u + ']', uniform[u]);
                }
            }
            // Array of arrays - but only arrays of vectors are allowed in this case
            else if (Array.isArray(uniform[0]) && typeof uniform[0][0] === 'number') {
                // float vectors (vec2, vec3, vec4)
                if (uniform[0].length >= 2 && uniform[0].length <= 4) {
                    // Set each vector in the array
                    for (u=0; u < uniform.length; u++) {
                        this.uniform(uniform[u].length + 'fv', name + '[' + u + ']', uniform[u]);
                    }
                }
                // else error?
            }
            // Array of structures
            else if (typeof uniform[0] === 'object') {
                for (u=0; u < uniform.length; u++) {
                    // For each element in the struct array, set each field, passing along current texture unit
                    this.setUniforms(uniform[u], name + '[' + u + ']', this.texture_unit);
                }
            }
        }
        // Boolean
        else if (typeof uniform === 'boolean') {
            this.uniform('1i', name, uniform);
        }
        // Texture
        else if (typeof uniform === 'string') {
            this.setTextureUniform(name, uniform);
        }
        // Structure
        else if (typeof uniform === 'object') {
            // Set each field in the struct, passing along current texture unit
            this.setUniforms(uniform, name, this.texture_unit);
        }

        // TODO: support other non-float types? (int, etc.)
    }
};

// Set a texture uniform, finds texture by name or creates a new one
GLProgram.prototype.setTextureUniform = function (uniform_name, texture_name) {
    var texture = GLTexture.textures[texture_name];
    if (texture == null) {
        texture = new GLTexture(this.gl, texture_name);
        texture.load(texture_name);
    }

    texture.bind(this.texture_unit);
    this.uniform('1i', uniform_name, this.texture_unit);
    this.texture_unit++; // TODO: track max texture units and log/throw errors
};

// ex: program.uniform('3f', 'position', x, y, z);
// TODO: only update uniforms when changed
GLProgram.prototype.uniform = function (method, name, ...values) // 'values' is a method-appropriate arguments list
{
    if (!this.compiled) {
        return;
    }

    var uniform = (this.uniforms[name] = this.uniforms[name] || {});
    uniform.name = name;
    uniform.location = uniform.location || this.gl.getUniformLocation(this.program, name);
    uniform.method = 'uniform' + method;
    uniform.values = values;
    this.updateUniform(name);
};

// Set a single uniform
GLProgram.prototype.updateUniform = function (name)
{
    if (!this.compiled) {
        return;
    }

    var uniform = this.uniforms[name];
    if (uniform == null || uniform.location == null) {
        return;
    }

    this.use();
    this.gl[uniform.method].apply(this.gl, [uniform.location].concat(uniform.values)); // call appropriate GL uniform method and pass through arguments
};

// Refresh uniform locations and set to last cached values
GLProgram.prototype.refreshUniforms = function ()
{
    if (!this.compiled) {
        return;
    }

    for (var u in this.uniforms) {
        this.uniforms[u].location = this.gl.getUniformLocation(this.program, u);
        this.updateUniform(u);
    }
};

GLProgram.prototype.refreshAttributes = function ()
{
    // var len = this.gl.getProgramParameter(this.program, this.gl.ACTIVE_ATTRIBUTES);
    // for (var i=0; i < len; i++) {
    //     var a = this.gl.getActiveAttrib(this.program, i);
    // }
    this.attribs = {};
};

// Get the location of a vertex attribute
GLProgram.prototype.attribute = function (name)
{
    if (!this.compiled) {
        return;
    }

    var attrib = (this.attribs[name] = this.attribs[name] || {});
    if (attrib.location != null) {
        return attrib;
    }

    attrib.name = name;
    attrib.location = this.gl.getAttribLocation(this.program, name);

    // var info = this.gl.getActiveAttrib(this.program, attrib.location);
    // attrib.type = info.type;
    // attrib.size = info.size;

    return attrib;
};

// Check for a uniform definition of 'name' in the provided GLSL source
// Simple regex check for 'uniform' keyword and var name, does not attempt to parse/extract GLSL
GLProgram.uniformDefined = function (name, source) {
    // Match, in order:
    // - the keyword 'uniform'
    // - at least one character that is anything except a semicolon, ;
    // - optionally, anything enclosed in curly braces, { ... } (an inline structure definition can go here)
    // - optionally, any # of characters that is not a semicolon, ;
    // - the name of the uniform

    var re = new RegExp('uniform[^;]+(?:\{[\s\S]*\})?[^;]*' + name, 'g');
    if (source.match(re)) {
        return true;
    }
    return false;
};
<|endoftext|>


<commit_before>const langs = ['en', 'it', 'de'];
let url = window.location.hash;
url = url.toString().replace('#', '');

$(window).on('load', function () {
	setTimeout(function () {
		$('.last').addClass('last-loaded');
	}, 2000);
	AOS.init();
});

const translate = function (jsdata) {
	$("[tkey]").each(function (index) {
		var strTr = jsdata [$(this).attr('tkey')];
		$(this).html(strTr);
	});
};

let langCode = navigator.language.substr(0, 2);

if (Cookies.get('lang') === undefined) {
	Cookies.set('lang', langCode, { expires: 365 }, { path: '' });
}

const cookieLang = Cookies.get('lang');

function changeLang(lang) {
	$.getJSON('lang/' + lang + '.json', translate);
	Cookies.set('lang', lang, { expires: 365 }, { path: '' });
}

if (url == 'de') changeLang('de');

else if (url == 'it') changeLang(it);

else if (url == 'en') changeLang('en');

else if (langs.indexOf(cookieLang) >= 0) changeLang(cookieLang);

else changeLang('en');

$('#changeEn').on('click', function () {
	changeLang('en');
});

$('#changeDe').on('click', function () {
	changeLang('de');
});

$('#changeIt').on('click', function () {
	changeLang('it');
});

$('#removeLang').on('click', function () {
	Cookies.remove('lang', { path: '' });
	window.location.reload();
});<commit_msg>Update main.js<commit_after>const langs = ['en', 'it', 'de'];
let url = window.location.hash;
url = url.toString().replace('#', '');

$(window).on('load', function () {
	setTimeout(function () {
		$('.last').addClass('last-loaded');
	}, 2000);
	AOS.init();
});

const translate = function (jsdata) {
	$("[tkey]").each(function (index) {
		let strTr = jsdata [$(this).attr('tkey')];
		$(this).html(strTr);
	});
};

let langCode = navigator.language.substr(0, 2);

if (Cookies.get('lang') === undefined) {
	Cookies.set('lang', langCode, { expires: 365 }, { path: '' });
}

const cookieLang = Cookies.get('lang');

function changeLang(lang) {
	$.getJSON('lang/' + lang + '.json', translate);
	Cookies.set('lang', lang, { expires: 365 }, { path: '' });
}

if (url == 'de') changeLang('de');

else if (url == 'it') changeLang(it);

else if (url == 'en') changeLang('en');

else if (langs.indexOf(cookieLang) >= 0) changeLang(cookieLang);

else changeLang('en');

$('#changeEn').on('click', function () {
	changeLang('en');
});

$('#changeDe').on('click', function () {
	changeLang('de');
});

$('#changeIt').on('click', function () {
	changeLang('it');
});

$('#removeLang').on('click', function () {
	Cookies.remove('lang', { path: '' });
	window.location.reload();
});
<|endoftext|>


<commit_before>'use babel'

import InfoView from './info-view'
import StatusView from './status-view'
import MoieClient from './moie-client'
import * as util from './util'
import { CompositeDisposable, BufferedProcess } from 'atom'

export default {

  config: {
    moieExec: {
      type: 'string',
      default: '/home/user/moie.jar',
      description: 'Full path to moie-server'
    },
    javaExec: {
      type: 'string',
      default: '/usr/bin/java',
      description: 'Full path to java; `whereis java`'
    },
    startServer: {
      type: 'boolean',
      default: false,
      description: 'Start a new instance of moie-server if none running'
    },
    interface: {
      type: "string",
      default: "localhost"
    },
    port: {
      type: "integer",
      default: 9001
    },
    compileMode: {
      type: 'string',
      default: 'on demand',
      enum: [
        'on demand',
        'while typing'
      ]
    }
  },

  infoView: null,
  statusView: null,
  infoPanel: null,
  statusTile: null,
  subscriptions: null,
  projectJson: "moie-project.json",
  triedAsSubprocess: false,

  consumeStatusBar(statusBar) {
    this.statusView = new StatusView()
    this.statusTile = statusBar.addLeftTile({'item': this.statusView.getRoot(), 'priority': 1})
  },

  activate(state) {
    this.infoView = new InfoView(state.infoViewState)
    this.infoPanel = atom.workspace.addBottomPanel({
      item: this.infoView.getRoot(),
      visible: false
    })

    this.client = new MoieClient(
        atom.config.get('Moie.interface'),
        atom.config.get('Moie.port'))
    this.markers = []

    // Events subscribed to in atom's system can be easily cleaned up with a CompositeDisposable
    this.subscriptions = new CompositeDisposable()

    // Register command that toggles this view
    this.subscriptions.add(atom.commands.add('atom-workspace', {
      'moie:connect': () => this.connect(),
      'moie:disconnect': () => this.disconnect(),
      'moie:compile-Project': () => {
        util.saveAllFiles()
        this.compileProject()
      },
      'moie:compile-Script': () => this.compileScript(),
      'moie:stop-Server': () => this.stopServer()
    }))

    //before closing the window; send a disconnect
    window.onbeforeunload = (e) => {
      this.disconnect()
    }

    atom.workspace.observeTextEditors(editor => {
      const self = this
      editor.onDidSave( (ev) => {
        if(atom.config.get('Moie.compileMode') == 'while typing')
          self.userSavedFile(ev)
      })
    })
  },

  deactivate() {
    this.modalPanel.destroy()
    this.subscriptions.dispose()
    this.moieView.destroy()
  },

  serialize() {
    return {
      infoViewState: this.infoView.serialize(),
      statusViewState: this.statusView.serialize(),
    }
  },

  connect() {
    const self = this
    const projects = atom.project.getDirectories()
    if(projects.length == 1) {
      const projectPath = projects[0]
      this.readProjectFileContent(projectPath, json => {
        this.client.connect(json, () => {
          atom.notifications.addSuccess("Mo|E connected")
          this.statusView.success("connected")
        }, (status,error) => {
          if(atom.config.get('Moie.startServer') && !this.triedAsSubprocess) {
            util.startMoieServer(() => this.connect())
            this.triedAsSubprocess = true
          } else {
            atom.notifications.addError(`Couldn't connect Mo|E to the server!`)
            this.statusView.error("disconnected")
          }
        })
      })
    } else {
      atom.notifications.addError("Can't handle multiple project-roots.\nUse 1 project")
      this.statusView.error("error")
    }
  },

  readProjectFileContent(rootDir, onSuccess) {
    const file = rootDir.getFile(this.projectJson)
    if(file.existsSync() && file.isFile()) {
      const file = rootDir.getFile(this.projectJson)
      file.read(true).then(onSuccess)
    } else {
      atom.notifications.addError(`Please generate a ${this.projectJson} in\n${rootDir.path}`)
    }
  },

  disconnect() {
    console.log("disconnect pressed")
    this.client.disconnect(() => {
      atom.notifications.addSuccess("Mo|E disconnected!")
      this.statusView.success("disconnected")
      this.infoPanel.hide()
    })
    this.clearErrors()
    this.triedAsSubprocess = false
  },

  stopServer() {
    this.client.stopServer(() => {
      this.statusView.success("disconnected")
      atom.notifications.addSuccess("Server stopped")
    })
    this.clearErrors()
  },

  compileProject() {
    this.clearErrors()
    const filepath = atom.workspace.getActiveTextEditor().getPath()
    if(util.isModelicaScript(filepath)) {
      this.client.compileScript(
        filepath,
        this.onCompileSuccess.bind(this),
        this.onCompileError.bind(this))
    } else {
      this.client.compile(
        this.onCompileSuccess.bind(this),
        this.onCompileError.bind(this)
      )
    }
  },

  compileScript() {
    this.clearErrors()
    const filepath = util.currentFile()
    if(util.isModelicaScript(filepath)) {
      this.client.compileScript(
        filepath,
        this.onCompileSuccess.bind(this),
        this.onCompileError.bind(this)
      )
    } else {
      this.client.compileScript(
        undefined,
        this.onCompileSuccess.bind(this),
        this.onCompileError.bind(this))
    }
  },

  onCompileSuccess(data) {
    const lines = data.map(util.createErrorLine)
    this.infoView.addLines(lines)
    if(lines.length != 0) {
      data.forEach(errorObj => {
        const errorRange = [[errorObj.start.line-1,errorObj.start.column],
        [errorObj.end.line-1,errorObj.end.column]]
        const promise = atom.workspace.open(errorObj.file, {activatePane:false})
        promise.then(this.highlightErrors(errorRange).bind(this))
      })
      this.infoPanel.show()
    }
    else
      this.statusView.success("compiled")
  },

  highlightErrors(range) {
    return (editor) => {
      const rangeMarker = editor.markBufferRange(range, {invalidate:'never'})
      this.markers.push(rangeMarker)
      editor.decorateMarker(rangeMarker, {type: 'line-number', class: 'line-number-error'})
      this.statusView.error("compile errors")
    }
  },

  onCompileError(xhr, status,error) {
    if(xhr.status == 500) {
      atom.notifications.addError("InternalServerError.. Restart server and try agin.")
      this.statusView.error("server-error")
    } else if(xhr.status == 404) {
      atom.notifications.addError(xhr.responseText)
      this.statusView.error("server-error")
    }
    else {
      atom.notifications.addError("Can't contact server.. Restart server and try agin.")
      this.statusView.error("disconnected")
    }
  },

  clearErrors() {
    this.markers.forEach(marker => marker.destroy())
    this.infoPanel.hide()
  },

  userSavedFile(ev) {
    const path = ev.path
    if(util.isModelicaFile(path)) {
      this.compileProject()
    }
  }
}
<commit_msg>Use util.currentFile()<commit_after>'use babel'

import InfoView from './info-view'
import StatusView from './status-view'
import MoieClient from './moie-client'
import * as util from './util'
import { CompositeDisposable, BufferedProcess } from 'atom'

export default {

  config: {
    moieExec: {
      type: 'string',
      default: '/home/user/moie.jar',
      description: 'Full path to moie-server'
    },
    javaExec: {
      type: 'string',
      default: '/usr/bin/java',
      description: 'Full path to java; `whereis java`'
    },
    startServer: {
      type: 'boolean',
      default: false,
      description: 'Start a new instance of moie-server if none running'
    },
    interface: {
      type: "string",
      default: "localhost"
    },
    port: {
      type: "integer",
      default: 9001
    },
    compileMode: {
      type: 'string',
      default: 'on demand',
      enum: [
        'on demand',
        'while typing'
      ]
    }
  },

  infoView: null,
  statusView: null,
  infoPanel: null,
  statusTile: null,
  subscriptions: null,
  projectJson: "moie-project.json",
  triedAsSubprocess: false,

  consumeStatusBar(statusBar) {
    this.statusView = new StatusView()
    this.statusTile = statusBar.addLeftTile({'item': this.statusView.getRoot(), 'priority': 1})
  },

  activate(state) {
    this.infoView = new InfoView(state.infoViewState)
    this.infoPanel = atom.workspace.addBottomPanel({
      item: this.infoView.getRoot(),
      visible: false
    })

    this.client = new MoieClient(
        atom.config.get('Moie.interface'),
        atom.config.get('Moie.port'))
    this.markers = []

    // Events subscribed to in atom's system can be easily cleaned up with a CompositeDisposable
    this.subscriptions = new CompositeDisposable()

    // Register command that toggles this view
    this.subscriptions.add(atom.commands.add('atom-workspace', {
      'moie:connect': () => this.connect(),
      'moie:disconnect': () => this.disconnect(),
      'moie:compile-Project': () => {
        util.saveAllFiles()
        this.compileProject()
      },
      'moie:compile-Script': () => this.compileScript(),
      'moie:stop-Server': () => this.stopServer()
    }))

    //before closing the window; send a disconnect
    window.onbeforeunload = (e) => {
      this.disconnect()
    }

    atom.workspace.observeTextEditors(editor => {
      const self = this
      editor.onDidSave( (ev) => {
        if(atom.config.get('Moie.compileMode') == 'while typing')
          self.userSavedFile(ev)
      })
    })
  },

  deactivate() {
    this.modalPanel.destroy()
    this.subscriptions.dispose()
    this.moieView.destroy()
  },

  serialize() {
    return {
      infoViewState: this.infoView.serialize(),
      statusViewState: this.statusView.serialize(),
    }
  },

  connect() {
    const self = this
    const projects = atom.project.getDirectories()
    if(projects.length == 1) {
      const projectPath = projects[0]
      this.readProjectFileContent(projectPath, json => {
        this.client.connect(json, () => {
          atom.notifications.addSuccess("Mo|E connected")
          this.statusView.success("connected")
        }, (status,error) => {
          if(atom.config.get('Moie.startServer') && !this.triedAsSubprocess) {
            util.startMoieServer(() => this.connect())
            this.triedAsSubprocess = true
          } else {
            atom.notifications.addError(`Couldn't connect Mo|E to the server!`)
            this.statusView.error("disconnected")
          }
        })
      })
    } else {
      atom.notifications.addError("Can't handle multiple project-roots.\nUse 1 project")
      this.statusView.error("error")
    }
  },

  readProjectFileContent(rootDir, onSuccess) {
    const file = rootDir.getFile(this.projectJson)
    if(file.existsSync() && file.isFile()) {
      const file = rootDir.getFile(this.projectJson)
      file.read(true).then(onSuccess)
    } else {
      atom.notifications.addError(`Please generate a ${this.projectJson} in\n${rootDir.path}`)
    }
  },

  disconnect() {
    console.log("disconnect pressed")
    this.client.disconnect(() => {
      atom.notifications.addSuccess("Mo|E disconnected!")
      this.statusView.success("disconnected")
      this.infoPanel.hide()
    })
    this.clearErrors()
    this.triedAsSubprocess = false
  },

  stopServer() {
    this.client.stopServer(() => {
      this.statusView.success("disconnected")
      atom.notifications.addSuccess("Server stopped")
    })
    this.clearErrors()
  },

  compileProject() {
    this.clearErrors()
    const filepath = util.currentFile()
    if(util.isModelicaScript(filepath)) {
      this.client.compileScript(
        filepath,
        this.onCompileSuccess.bind(this),
        this.onCompileError.bind(this))
    } else {
      this.client.compile(
        this.onCompileSuccess.bind(this),
        this.onCompileError.bind(this)
      )
    }
  },

  compileScript() {
    this.clearErrors()
    const filepath = util.currentFile()
    if(util.isModelicaScript(filepath)) {
      this.client.compileScript(
        filepath,
        this.onCompileSuccess.bind(this),
        this.onCompileError.bind(this)
      )
    } else {
      this.client.compileScript(
        undefined,
        this.onCompileSuccess.bind(this),
        this.onCompileError.bind(this))
    }
  },

  onCompileSuccess(data) {
    const lines = data.map(util.createErrorLine)
    this.infoView.addLines(lines)
    if(lines.length != 0) {
      data.forEach(errorObj => {
        const errorRange = [[errorObj.start.line-1,errorObj.start.column],
        [errorObj.end.line-1,errorObj.end.column]]
        const promise = atom.workspace.open(errorObj.file, {activatePane:false})
        promise.then(this.highlightErrors(errorRange).bind(this))
      })
      this.infoPanel.show()
    }
    else
      this.statusView.success("compiled")
  },

  highlightErrors(range) {
    return (editor) => {
      const rangeMarker = editor.markBufferRange(range, {invalidate:'never'})
      this.markers.push(rangeMarker)
      editor.decorateMarker(rangeMarker, {type: 'line-number', class: 'line-number-error'})
      this.statusView.error("compile errors")
    }
  },

  onCompileError(xhr, status,error) {
    if(xhr.status == 500) {
      atom.notifications.addError("InternalServerError.. Restart server and try agin.")
      this.statusView.error("server-error")
    } else if(xhr.status == 404) {
      atom.notifications.addError(xhr.responseText)
      this.statusView.error("server-error")
    }
    else {
      atom.notifications.addError("Can't contact server.. Restart server and try agin.")
      this.statusView.error("disconnected")
    }
  },

  clearErrors() {
    this.markers.forEach(marker => marker.destroy())
    this.infoPanel.hide()
  },

  userSavedFile(ev) {
    const path = ev.path
    if(util.isModelicaFile(path)) {
      this.compileProject()
    }
  }
}
<|endoftext|>


<commit_before>/*global GLTexture */
// Texture management
import Utils from '../utils';
import WorkerBroker from '../worker_broker';
import log from 'loglevel';

// Global set of textures, by name
GLTexture.textures = {};

// GL texture wrapper object for keeping track of a global set of textures, keyed by a unique user-defined name
export default function GLTexture (gl, name, options = {}) {
    this.gl = gl;
    this.texture = gl.createTexture();
    if (this.texture) {
        this.valid = true;
    }
    this.bind(0);
    this.image = null;      // an Image object/element that is the source for this texture
    this.loading = null;    // a Promise object to track the loading state of this texture

    // Default to a 1-pixel black texture so we can safely render while we wait for an image to load
    // See: http://stackoverflow.com/questions/19722247/webgl-wait-for-texture-to-load
    this.setData(1, 1, new Uint8Array([0, 0, 0, 255]), { filtering: 'nearest' });

    // TODO: better support for non-URL sources: canvas/video elements, raw pixel buffers

    this.name = name;

    // Destroy previous texture if present
    if (GLTexture.textures[this.name]) {
        GLTexture.textures[this.name].destroy();
    }

    GLTexture.textures[this.name] = this;

    this.sprites = options.sprites;
}

// Destroy a single texture instance
GLTexture.prototype.destroy = function () {
    if (!this.valid) {
        return;
    }
    this.gl.deleteTexture(this.texture);
    this.texture = null;
    delete this.data;
    this.data = null;
    delete GLTexture.textures[this.name];
    this.valid = false;
};

// Destroy all texture instances for a given GL context
GLTexture.destroy = function (gl) {
    var textures = Object.keys(GLTexture.textures);
    for (var t of textures) {
        var texture = GLTexture.textures[t];
        if (texture.gl === gl) {
            log.trace(`destroying GLTexture ${texture.name}`);
            texture.destroy();
        }
    }
};

GLTexture.prototype.bind = function (unit) {
    if (!this.valid) {
        return;
    }
    this.gl.activeTexture(this.gl.TEXTURE0 + unit);
    this.gl.bindTexture(this.gl.TEXTURE_2D, this.texture);
};

// Loads a texture from a URL
GLTexture.prototype.load = function (url, options = {}) {
    if (!this.valid) {
        return;
    }

    this.loading = new Promise((resolve, reject) => {
        this.image = new Image();
        this.image.onload = () => {
            this.width = this.image.width;
            this.height = this.image.height;
            this.data = null; // mutually exclusive with direct data buffer textures
            this.update(options);
            this.setTextureFiltering(options);
            resolve(this);
        };
        this.image.src = url;
        // TODO: error/promise reject
    });
    return this.loading;
};

// Sets texture to a raw image buffer
GLTexture.prototype.setData = function (width, height, data, options = {}) {
    this.width = width;
    this.height = height;
    this.data = data;
    this.image = null; // mutually exclusive with image element-based textures

    this.update(options);
    this.setTextureFiltering(options);
};

// Uploads current image or buffer to the GPU (can be used to update animated textures on the fly)
GLTexture.prototype.update = function (options = {}) {
    if (!this.valid) {
        return;
    }

    this.bind(0);
    this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL, (options.UNPACK_FLIP_Y_WEBGL === false ? false : true));
    this.gl.pixelStorei(this.gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, options.UNPACK_PREMULTIPLY_ALPHA_WEBGL || false);

    // Image element
    if (this.image && this.image.complete) {
        this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA, this.gl.RGBA, this.gl.UNSIGNED_BYTE, this.image);
    }
    // Raw image buffer
    else if (this.width && this.height) { // NOTE: this.data can be null, to zero out texture
        this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA, this.width, this.height, 0, this.gl.RGBA, this.gl.UNSIGNED_BYTE, this.data);
    }
};

// Determines appropriate filtering mode
// Assumes texture to be operated on is already bound
GLTexture.prototype.setTextureFiltering = function (options = {}) {
    if (!this.valid) {
        return;
    }

    options.filtering = options.filtering || 'mipmap'; // default to mipmaps for power-of-2 textures
    var gl = this.gl;

    // For power-of-2 textures, the following presets are available:
    // mipmap: linear blend from nearest mip
    // linear: linear blend from original image (no mips)
    // nearest: nearest pixel from original image (no mips, 'blocky' look)
    if (Utils.isPowerOf2(this.width) && Utils.isPowerOf2(this.height)) {
        this.power_of_2 = true;
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, options.TEXTURE_WRAP_S || (options.repeat && gl.REPEAT) || gl.CLAMP_TO_EDGE);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, options.TEXTURE_WRAP_T || (options.repeat && gl.REPEAT) || gl.CLAMP_TO_EDGE);

        if (options.filtering === 'mipmap') {
            log.trace('power-of-2 MIPMAP');
            this.filtering = 'mipmap';
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST); // TODO: use trilinear filtering by defualt instead?
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
            gl.generateMipmap(gl.TEXTURE_2D);
        }
        else if (options.filtering === 'linear') {
            log.trace('power-of-2 LINEAR');
            this.filtering = 'linear';
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
        }
        else if (options.filtering === 'nearest') {
            log.trace('power-of-2 NEAREST');
            this.filtering = 'nearest';
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
        }
    }
    else {
        // WebGL has strict requirements on non-power-of-2 textures:
        // No mipmaps and must clamp to edge
        this.power_of_2 = false;
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);

        if (options.filtering === 'nearest') {
            log.trace('power-of-2 NEAREST');
            this.filtering = 'nearest';
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
        }
        else { // default to linear for non-power-of-2 textures
            log.trace('power-of-2 LINEAR');
            this.filtering = 'linear';
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
        }
    }
};

// Static/class methods

// Get metadata for a texture by name
// Returns via promise, in case texture is still loading
// Can be called on main thread from worker, to sync texture info to worker
GLTexture.getInfo = function (name) {
    // Get info for all textures by default
    if (!name) {
        name = Object.keys(GLTexture.textures);
    }

    // Get multiple textures
    if (Array.isArray(name)) {
        return Promise.all(name.map(n => GLTexture.getInfo(n)));
    }

    // Get single texture
    var tex = GLTexture.textures[name];
    if (tex) {
        // Wait for this texture to finish loading, or return immediately
        var loading = tex.loading || Promise.resolve(tex);
        return loading.then(() => {
            // Return a subset of texture info
            // (compatible w/structured cloning, suitable for passing to a worker)
            return {
                name: tex.name,
                width: tex.width,
                height: tex.height,
                sprites: tex.sprites,
                filtering: tex.filtering,
                power_of_2: tex.power_of_2,
                valid: tex.valid
            };
        });
    }
    else {
        // No texture found
        return Promise.resolve(null);
    }
};

// Sync texture info to worker
// Called from worker, gets info on one or more textures info from main thread via remote call, then stores it
// locally in worker. 'textures' can be an array of texture names to sync, or if null, all textures are synced.
GLTexture.syncTexturesToWorker = function (names) {
    return WorkerBroker.postMessage('GLTexture', 'getInfo', names).
        then(textures => {
            for (var tex of textures) {
                GLTexture.textures[tex.name] = tex;
            }
            return GLTexture.textures;
        });
};
<commit_msg>add option to assign a canvas element to a texture (only internally in the wrapper, could possibly be exposed in stylesheet though)<commit_after>/*global GLTexture */
// Texture management
import Utils from '../utils';
import WorkerBroker from '../worker_broker';
import log from 'loglevel';

// Global set of textures, by name
GLTexture.textures = {};

// GL texture wrapper object for keeping track of a global set of textures, keyed by a unique user-defined name
export default function GLTexture (gl, name, options = {}) {
    this.gl = gl;
    this.texture = gl.createTexture();
    if (this.texture) {
        this.valid = true;
    }
    this.bind(0);
    this.image = null;      // an Image object/element that is the source for this texture
    this.canvas = null;     // a Canvas object/element that is the source for this texture
    this.loading = null;    // a Promise object to track the loading state of this texture

    // Default to a 1-pixel black texture so we can safely render while we wait for an image to load
    // See: http://stackoverflow.com/questions/19722247/webgl-wait-for-texture-to-load
    this.setData(1, 1, new Uint8Array([0, 0, 0, 255]), { filtering: 'nearest' });

    // TODO: better support for non-URL sources: canvas/video elements, raw pixel buffers

    this.name = name;

    // Destroy previous texture if present
    if (GLTexture.textures[this.name]) {
        GLTexture.textures[this.name].destroy();
    }

    GLTexture.textures[this.name] = this;

    this.sprites = options.sprites;
}

// Destroy a single texture instance
GLTexture.prototype.destroy = function () {
    if (!this.valid) {
        return;
    }
    this.gl.deleteTexture(this.texture);
    this.texture = null;
    delete this.data;
    this.data = null;
    delete GLTexture.textures[this.name];
    this.valid = false;
};

// Destroy all texture instances for a given GL context
GLTexture.destroy = function (gl) {
    var textures = Object.keys(GLTexture.textures);
    for (var t of textures) {
        var texture = GLTexture.textures[t];
        if (texture.gl === gl) {
            log.trace(`destroying GLTexture ${texture.name}`);
            texture.destroy();
        }
    }
};

GLTexture.prototype.bind = function (unit) {
    if (!this.valid) {
        return;
    }
    this.gl.activeTexture(this.gl.TEXTURE0 + unit);
    this.gl.bindTexture(this.gl.TEXTURE_2D, this.texture);
};

// Loads a texture from a URL
GLTexture.prototype.load = function (url, options = {}) {
    if (!this.valid) {
        return;
    }

    this.loading = new Promise((resolve, reject) => {
        this.image = new Image();
        this.image.onload = () => {
            this.width = this.image.width;
            this.height = this.image.height;
            this.update(options);
            this.setTextureFiltering(options);

            this.canvas = null; // mutually exclusive with other types
            this.data = null;

            resolve(this);
        };
        this.image.src = url;
        // TODO: error/promise reject
    });
    return this.loading;
};

// Sets texture to a raw image buffer
GLTexture.prototype.setData = function (width, height, data, options = {}) {
    this.width = width;
    this.height = height;
    this.data = data;

    this.image = null; // mutually exclusive with other types
    this.canvas = null;

    this.update(options);
    this.setTextureFiltering(options);
};

// Sets the texture to track a canvas element
GLTexture.prototype.setCanvas = function (canvas, options) {
    this.canvas = canvas;
    this.width = this.canvas.width;
    this.height = this.canvas.height;
    this.update(options);
    this.setTextureFiltering(options);

    this.image = null; // mutually exclusive with other types
    this.data = null;
};

// Uploads current image or buffer to the GPU (can be used to update animated textures on the fly)
GLTexture.prototype.update = function (options = {}) {
    if (!this.valid) {
        return;
    }

    this.bind(0);
    this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL, (options.UNPACK_FLIP_Y_WEBGL === false ? false : true));
    this.gl.pixelStorei(this.gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, options.UNPACK_PREMULTIPLY_ALPHA_WEBGL || false);

    // Image element
    if (this.image && this.image.complete) {
        this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA, this.gl.RGBA, this.gl.UNSIGNED_BYTE, this.image);
    }
    // Canvas element
    else if (this.canvas) {
        this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA, this.gl.RGBA, this.gl.UNSIGNED_BYTE, this.canvas);
    }
    // Raw image buffer
    else if (this.width && this.height) { // NOTE: this.data can be null, to zero out texture
        this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA, this.width, this.height, 0, this.gl.RGBA, this.gl.UNSIGNED_BYTE, this.data);
    }
};

// Determines appropriate filtering mode
// Assumes texture to be operated on is already bound
GLTexture.prototype.setTextureFiltering = function (options = {}) {
    if (!this.valid) {
        return;
    }

    options.filtering = options.filtering || 'mipmap'; // default to mipmaps for power-of-2 textures
    var gl = this.gl;

    // For power-of-2 textures, the following presets are available:
    // mipmap: linear blend from nearest mip
    // linear: linear blend from original image (no mips)
    // nearest: nearest pixel from original image (no mips, 'blocky' look)
    if (Utils.isPowerOf2(this.width) && Utils.isPowerOf2(this.height)) {
        this.power_of_2 = true;
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, options.TEXTURE_WRAP_S || (options.repeat && gl.REPEAT) || gl.CLAMP_TO_EDGE);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, options.TEXTURE_WRAP_T || (options.repeat && gl.REPEAT) || gl.CLAMP_TO_EDGE);

        if (options.filtering === 'mipmap') {
            log.trace('power-of-2 MIPMAP');
            this.filtering = 'mipmap';
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST); // TODO: use trilinear filtering by defualt instead?
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
            gl.generateMipmap(gl.TEXTURE_2D);
        }
        else if (options.filtering === 'linear') {
            log.trace('power-of-2 LINEAR');
            this.filtering = 'linear';
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
        }
        else if (options.filtering === 'nearest') {
            log.trace('power-of-2 NEAREST');
            this.filtering = 'nearest';
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
        }
    }
    else {
        // WebGL has strict requirements on non-power-of-2 textures:
        // No mipmaps and must clamp to edge
        this.power_of_2 = false;
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);

        if (options.filtering === 'nearest') {
            log.trace('power-of-2 NEAREST');
            this.filtering = 'nearest';
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
        }
        else { // default to linear for non-power-of-2 textures
            log.trace('power-of-2 LINEAR');
            this.filtering = 'linear';
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
        }
    }
};

// Static/class methods

// Get metadata for a texture by name
// Returns via promise, in case texture is still loading
// Can be called on main thread from worker, to sync texture info to worker
GLTexture.getInfo = function (name) {
    // Get info for all textures by default
    if (!name) {
        name = Object.keys(GLTexture.textures);
    }

    // Get multiple textures
    if (Array.isArray(name)) {
        return Promise.all(name.map(n => GLTexture.getInfo(n)));
    }

    // Get single texture
    var tex = GLTexture.textures[name];
    if (tex) {
        // Wait for this texture to finish loading, or return immediately
        var loading = tex.loading || Promise.resolve(tex);
        return loading.then(() => {
            // Return a subset of texture info
            // (compatible w/structured cloning, suitable for passing to a worker)
            return {
                name: tex.name,
                width: tex.width,
                height: tex.height,
                sprites: tex.sprites,
                filtering: tex.filtering,
                power_of_2: tex.power_of_2,
                valid: tex.valid
            };
        });
    }
    else {
        // No texture found
        return Promise.resolve(null);
    }
};

// Sync texture info to worker
// Called from worker, gets info on one or more textures info from main thread via remote call, then stores it
// locally in worker. 'textures' can be an array of texture names to sync, or if null, all textures are synced.
GLTexture.syncTexturesToWorker = function (names) {
    return WorkerBroker.postMessage('GLTexture', 'getInfo', names).
        then(textures => {
            for (var tex of textures) {
                GLTexture.textures[tex.name] = tex;
            }
            return GLTexture.textures;
        });
};
<|endoftext|>


<commit_before>var express = require('express');
var app = express();
var http = require('http').Server(app);
var https = require('https');
var basicAuth = require('basic-auth');
var fs = require('fs');
var io = require('socket.io')(http);
var screenshot = require('./screenshot.js')();
var MongoClient = require('mongodb').MongoClient;
var queryApi = require('./query');

var config = require('./_config.json');

if (config.ssl.enabled){
	var privateKey  = fs.readFileSync(config.ssl.privateKey, 'utf8');
	var certificate = fs.readFileSync(config.ssl.certificate, 'utf8');
	var credentials = {key: privateKey, cert: certificate};
	var httpsServer = https.createServer(credentials, app);
}

// Establish Database Connection
var DB = null;
MongoClient.connect(config.mongoDB.connection, function(err, connection) {
	if(err) throw err;
	DB = connection;
});

// ################### Express Webserver ###################

var auth = function (req, res, next) {
	function unauthorized(res) {
		res.set('WWW-Authenticate', 'Basic realm=Authorization Required');
		return res.sendStatus(401);
	}
	var user = basicAuth(req);
	if (!user || !user.name || !user.pass) {
		return unauthorized(res);
	}
	if (user.name === config.basicAuth.user && user.pass === config.basicAuth.pass) {
		return next();
	} else {
		return unauthorized(res);
	}
};

// Send CORS Headers
app.use(function (req, res, next) {
	res.setHeader('Access-Control-Allow-Origin', '*');
	next();
});

app.use('/app', auth, express.static(__dirname + '/../frontend'));
app.use('/client', auth, express.static(__dirname + '/../client'));
app.use('/screenshots', auth, express.static(__dirname + '/../backend/screenshots'));

app.get('/', function(req, res){
	res.send('
wTrack Server
Admin Tool
API');
});

// Query JSON API
app.get('/api', auth, queryApi._documentation);

// auto generate api routes
Object.keys(queryApi).forEach(function(endpoint) {
	// ignore endpoints starting with _
	if (endpoint.charAt(0) != '_'){
		app.get('/api/'+endpoint, auth, queryApi[endpoint]);
	}
});

// finally 404 Route
app.get('*', function(req, res){
	res.status(404).send('
Not Found

The requested URL was not found on this server.
');
});

// ################### Websockets ###################

io.on('connection', function(socket){
	socket.on('trackedEvent', function(msg){
		var eventData = JSON.parse(msg);

		var imageName = null;
/*		if (eventData && eventData.data && eventData.data.html){
			imageName = 'screenshots/wbl_' + Date.now() + '.png';
			screenshot.fromHTML(eventData.data, imageName);
		}*/

		// add image url and send to loggers
		var copy = JSON.parse(JSON.stringify(eventData));
		copy.data.screenshotUrl = imageName;
		copy.data.html = null;
		io.emit('trackedEvent', JSON.stringify(copy));

		// persist data in DB
		DB.collection('log').insert(copy, function(err, docs) {});
	});
});

http.listen(config.webserver.port, function(){
	console.log('http listening on *:'+config.webserver.port);
});

if (config.ssl.enabled) {
	httpsServer.listen(config.webserver.portSSL, function () {
		console.log('https listening on *:' + config.webserver.portSSL);
	});
}
<commit_msg>disable auth for client libraries<commit_after>var express = require('express');
var app = express();
var http = require('http').Server(app);
var https = require('https');
var basicAuth = require('basic-auth');
var fs = require('fs');
var io = require('socket.io')(http);
var screenshot = require('./screenshot.js')();
var MongoClient = require('mongodb').MongoClient;
var queryApi = require('./query');

var config = require('./_config.json');

if (config.ssl.enabled){
	var privateKey  = fs.readFileSync(config.ssl.privateKey, 'utf8');
	var certificate = fs.readFileSync(config.ssl.certificate, 'utf8');
	var credentials = {key: privateKey, cert: certificate};
	var httpsServer = https.createServer(credentials, app);
}

// Establish Database Connection
var DB = null;
MongoClient.connect(config.mongoDB.connection, function(err, connection) {
	if(err) throw err;
	DB = connection;
});

// ################### Express Webserver ###################

var auth = function (req, res, next) {
	function unauthorized(res) {
		res.set('WWW-Authenticate', 'Basic realm=Authorization Required');
		return res.sendStatus(401);
	}
	var user = basicAuth(req);
	if (!user || !user.name || !user.pass) {
		return unauthorized(res);
	}
	if (user.name === config.basicAuth.user && user.pass === config.basicAuth.pass) {
		return next();
	} else {
		return unauthorized(res);
	}
};

// Send CORS Headers
app.use(function (req, res, next) {
	res.setHeader('Access-Control-Allow-Origin', '*');
	next();
});

app.use('/client', express.static(__dirname + '/../client'));
app.use('/app', auth, express.static(__dirname + '/../frontend'));
app.use('/screenshots', auth, express.static(__dirname + '/../backend/screenshots'));

app.get('/', function(req, res){
	res.send('
wTrack Server
Admin Tool
API');
});

// Query JSON API
app.get('/api', auth, queryApi._documentation);

// auto generate api routes
Object.keys(queryApi).forEach(function(endpoint) {
	// ignore endpoints starting with _
	if (endpoint.charAt(0) != '_'){
		app.get('/api/'+endpoint, auth, queryApi[endpoint]);
	}
});

// finally 404 Route
app.get('*', function(req, res){
	res.status(404).send('
Not Found

The requested URL was not found on this server.
');
});

// ################### Websockets ###################

io.on('connection', function(socket){
	socket.on('trackedEvent', function(msg){
		var eventData = JSON.parse(msg);

		var imageName = null;
/*		if (eventData && eventData.data && eventData.data.html){
			imageName = 'screenshots/wbl_' + Date.now() + '.png';
			screenshot.fromHTML(eventData.data, imageName);
		}*/

		// add image url and send to loggers
		var copy = JSON.parse(JSON.stringify(eventData));
		copy.data.screenshotUrl = imageName;
		copy.data.html = null;
		io.emit('trackedEvent', JSON.stringify(copy));

		// persist data in DB
		DB.collection('log').insert(copy, function(err, docs) {});
	});
});

http.listen(config.webserver.port, function(){
	console.log('http listening on *:'+config.webserver.port);
});

if (config.ssl.enabled) {
	httpsServer.listen(config.webserver.portSSL, function () {
		console.log('https listening on *:' + config.webserver.portSSL);
	});
}
<|endoftext|>