<commit_before>// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt

frappe.provide("erpnext.projects");

cur_frm.add_fetch("project", "company", "company");

frappe.ui.form.on("Task", {
	refresh: function(frm) {
		var doc = frm.doc;
		if(doc.__islocal) {
			if(!frm.doc.exp_end_date) {
				frm.set_value("exp_end_date", frappe.datetime.add_days(new Date(), 7));
			}
		}

		if(!doc.__islocal) {
			if(frappe.model.can_read("Timesheet")) {
				frm.add_custom_button(__("Timesheet"), function() {
					frappe.route_options = {"project": doc.project, "task": doc.name}
					frappe.set_route("List", "Timesheet");
				}, __("View"), true);
			}
			if(frappe.model.can_read("Expense Claim")) {
				frm.add_custom_button(__("Expense Claims"), function() {
					frappe.route_options = {"project": doc.project, "task": doc.name}
					frappe.set_route("List", "Expense Claim");
				}, __("View"), true);
			}

			if(frm.perm[0].write) {
				if(frm.doc.status!=="Closed" && frm.doc.status!=="Cancelled") {
					frm.add_custom_button(__("Close"), function() {
						frm.set_value("status", "Closed");
						frm.save();
					});
				} else {
					frm.add_custom_button(__("Reopen"), function() {
						frm.set_value("status", "Open");
						frm.save();
					});
				}
			}
		}
	},

	setup: function(frm) {
		frm.fields_dict.project.get_query = function() {
			return {
				query: "erpnext.projects.doctype.task.task.get_project"
			}
		};
	},

	project: function(frm) {
		if(frm.doc.project) {
			return get_server_fields('get_project_details', '','', frm.doc, frm.doc.doctype,
				frm.doc.name, 1);
		}
	},

	validate: function(frm) {
		frm.doc.project && frappe.model.remove_from_locals("Project",
			frm.doc.project);
	},

});

cur_frm.add_fetch('task', 'subject', 'subject');
<commit_msg>Show only Project tasks in depends on tasks<commit_after>// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt

frappe.provide("erpnext.projects");

cur_frm.add_fetch("project", "company", "company");

frappe.ui.form.on("Task", {
	refresh: function(frm) {
		var doc = frm.doc;
		if(doc.__islocal) {
			if(!frm.doc.exp_end_date) {
				frm.set_value("exp_end_date", frappe.datetime.add_days(new Date(), 7));
			}
		}

		if(!doc.__islocal) {
			if(frappe.model.can_read("Timesheet")) {
				frm.add_custom_button(__("Timesheet"), function() {
					frappe.route_options = {"project": doc.project, "task": doc.name}
					frappe.set_route("List", "Timesheet");
				}, __("View"), true);
			}
			if(frappe.model.can_read("Expense Claim")) {
				frm.add_custom_button(__("Expense Claims"), function() {
					frappe.route_options = {"project": doc.project, "task": doc.name}
					frappe.set_route("List", "Expense Claim");
				}, __("View"), true);
			}

			if(frm.perm[0].write) {
				if(frm.doc.status!=="Closed" && frm.doc.status!=="Cancelled") {
					frm.add_custom_button(__("Close"), function() {
						frm.set_value("status", "Closed");
						frm.save();
					});
				} else {
					frm.add_custom_button(__("Reopen"), function() {
						frm.set_value("status", "Open");
						frm.save();
					});
				}
			}
		}
	},

	setup: function(frm) {
		frm.fields_dict.project.get_query = function() {
			return {
				query: "erpnext.projects.doctype.task.task.get_project"
			}
		};
	},

	project: function(frm) {
		if(frm.doc.project) {
			return get_server_fields('get_project_details', '','', frm.doc, frm.doc.doctype,
				frm.doc.name, 1);
		}
	},

	validate: function(frm) {
		frm.doc.project && frappe.model.remove_from_locals("Project",
			frm.doc.project);
	},

});

cur_frm.fields_dict['depends_on'].grid.get_field('task').get_query = function(doc) {
	if(doc.project) {
		return {
			filters: {'project': doc.project}
		}
	}
}

cur_frm.add_fetch('task', 'subject', 'subject');
<|endoftext|>


<commit_before>#!/usr/bin/env node

/*
  Get Basal Information

  Released under MIT license. See the accompanying LICENSE.txt file for
  full terms and conditions

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  THE SOFTWARE.

*/

var generate = require('oref0/lib/profile/');
function usage ( ) {
        console.log('usage: ', process.argv.slice(0, 2), '<pump_settings.json> <bg_targets.json> <insulin_sensitivities.json> <basal_profile.json> [<preferences.json>] [<carb_ratios.json>] [<temptargets.json>] [--model model.json] [--autotune autotune.json]');
}

function exportDefaults () {
	var defaults = generate.displayedDefaults();
	console.log(JSON.stringify(defaults, null, '\t'));
}

function updatePreferences (prefs) {
	var defaults = generate.displayedDefaults();

	// check for any displayedDefaults missing from current prefs and add from defaults

    for (var pref in defaults) {
      if (defaults.hasOwnProperty(pref) && !prefs.hasOwnProperty(pref)) {
        prefs[pref] = defaults[pref];
      }
    }

	console.log(JSON.stringify(prefs, null, '\t'));
}

if (!module.parent) {

    var argv = require('yargs')
      .usage("$0 pump_settings.json bg_targets.json insulin_sensitivities.json basal_profile.json [preferences.json] [<carb_ratios.json>] [<temptargets.json>] [--model model.json] [--autotune autotune.json]")
      .demand(4)
      .option('model', {
        alias: 'm',
        describe: "Pump model response",
        default: false
      })
      .option('autotune', {
        alias: 'a',
        describe: "Autotuned profile.json",
        default: false
      })
      .strict(true)
      .help('help')
      .option('exportDefaults', {
        describe: "Show typically-adjusted default preference values",
        default: false
      })
      .option('updatePreferences', {
        describe: "Check for any keys missing from current prefs and add from defaults",
        default: false
      })

    var params = argv.argv;
    var pumpsettings_input = params._[0]
    if ([null, '--help', '-h', 'help'].indexOf(pumpsettings_input) > 0) {
      usage( );
      process.exit(0);
    }
    if (params.exportDefaults) {
        exportDefaults();
        process.exit(0);
    }
    if (params.updatePreferences) {
        var preferences = {};
        var cwd = process.cwd()
        preferences = require(cwd + '/' + params.updatePreferences);
        updatePreferences(preferences);
        process.exit(0);
    }

    var bgtargets_input = params._[1]
    var isf_input = params._[2]
    var basalprofile_input = params._[3]
    var preferences_input = params._[4]
    var carbratio_input = params._[5]
    var temptargets_input = params._[6]
    var model_input = params.model;
    var autotune_input = params.autotune;

    if (!pumpsettings_input || !bgtargets_input || !isf_input || !basalprofile_input) {
        usage( );
        process.exit(1);
    }

    var cwd = process.cwd()
    var pumpsettings_data = require(cwd + '/' + pumpsettings_input);
    var bgtargets_data = require(cwd + '/' + bgtargets_input);
    if (bgtargets_data.units !== 'mg/dL') {
        if (bgtargets_data.units == 'mmol/L') {
            for (var i = 0, len = bgtargets_data.targets.length; i < len; i++) {
                bgtargets_data.targets[i].high = bgtargets_data.targets[i].high * 18;
                bgtargets_data.targets[i].low = bgtargets_data.targets[i].low * 18;
            }
            bgtargets_data.units = 'mg/dL';
        } else {
            console.log('BG Target data is expected to be expressed in mg/dL or mmol/L.'
                 , 'Found', bgtargets_data.units, 'in', bgtargets_input, '.');
            process.exit(2);
        }
    }

    var isf_data = require(cwd + '/' + isf_input);
    if (isf_data.units !== 'mg/dL') {
        if (isf_data.units == 'mmol/L') {
            for (var i = 0, len = isf_data.sensitivities.length; i < len; i++) {
                isf_data.sensitivities[i].sensitivity = isf_data.sensitivities[i].sensitivity * 18;
            }
            isf_data.units = 'mg/dL';
        } else {
            console.log('ISF is expected to be expressed in mg/dL or mmol/L.'
                    , 'Found', isf_data.units, 'in', isf_input, '.');
            process.exit(2);
        }
    }
    var basalprofile_data = require(cwd + '/' + basalprofile_input);

    var preferences = {};
    if (typeof preferences_input != 'undefined') {
        preferences = require(cwd + '/' + preferences_input);
    }
    var fs = require('fs');

    var model_data = { }
    if (params.model) {
      try {
        model_string = fs.readFileSync(model_input, 'utf8');
        model_data = model_string.replace(/\"/gi, '');
      } catch (e) {
        var msg = { error: e, msg: "Could not parse model_data", file: model_input};
        console.error(msg.msg);
        console.log(JSON.stringify(msg));
        process.exit(1);
      }
    }
    var autotune_data = { }
    if (params.autotune) {
      try {
        autotune_data = JSON.parse(fs.readFileSync(autotune_input, 'utf8'));

      } catch (e) {
        var msg = { error: e, msg: "Could not parse autotune_data", file: autotune_input};
        console.error(msg.msg);
        // Continue and output a non-autotuned profile if we don't have autotune_data
        //console.log(JSON.stringify(msg));
        //process.exit(1);
      }
    }

    var carbratio_data = { };
    //console.log("carbratio_input",carbratio_input);
    if (typeof carbratio_input != 'undefined') {
        try {
            carbratio_data = JSON.parse(fs.readFileSync(carbratio_input, 'utf8'));

        } catch (e) {
            var msg = { error: e, msg: "Could not parse carbratio_data. Feature Meal Assist enabled but cannot find required carb_ratios.", file: carbratio_input };
            console.error(msg.msg);
            console.log(JSON.stringify(msg));
            process.exit(1);
        }
        var errors = [ ];

        if (!(carbratio_data.schedule && carbratio_data.schedule[0].start && carbratio_data.schedule[0].ratio)) {
          errors.push({msg: "Carb ratio data should have an array called schedule with a start and ratio fields.", file: carbratio_input, data: carbratio_data});
        } else {
        }
        if (carbratio_data.units != 'grams' && carbratio_data.units != 'exchanges')  {
          errors.push({msg: "Carb ratio should have units field set to 'grams' or 'exchanges'.", file: carbratio_input, data: carbratio_data});
        }
        if (errors.length) {

          errors.forEach(function (msg) {
            console.error(msg.msg);
          });
          console.log(JSON.stringify(errors));
          process.exit(1);
        }
    }
    var temptargets_data = { };
    if (typeof temptargets_input != 'undefined') {
        try {
            temptargets_data = JSON.parse(fs.readFileSync(temptargets_input, 'utf8'));
        } catch (e) {
            console.error("Could not parse temptargets_data.");
        }
    }

    //console.log(carbratio_data);
    var inputs = { };

    //add all preferences to the inputs
    for (var pref in preferences) {
      if (preferences.hasOwnProperty(pref)) {
        inputs[pref] = preferences[pref];
      }
    }

    //make sure max_iob is set or default to 0
    inputs.max_iob = inputs.max_iob || 0;

    //set these after to make sure nothing happens if they are also set in preferences
    inputs.settings = pumpsettings_data;
    inputs.targets = bgtargets_data;
    inputs.basals = basalprofile_data;
    inputs.isf = isf_data;
    inputs.carbratio = carbratio_data;
    inputs.temptargets = temptargets_data;
    inputs.model = model_data;
    inputs.autotune = autotune_data;

    if (autotune_data) {
        if (autotune_data.basalprofile) { inputs.basals = autotune_data.basalprofile; }
        if (autotune_data.isfProfile) { inputs.isf = autotune_data.isfProfile; }
        if (autotune_data.carb_ratio) { inputs.carbratio.schedule[0].ratio = autotune_data.carb_ratio; }
    }
    var profile = generate(inputs);

    console.log(JSON.stringify(profile));

}
<commit_msg>revert change that broke --exportDefaults and --updatePreferences<commit_after>#!/usr/bin/env node

/*
  Get Basal Information

  Released under MIT license. See the accompanying LICENSE.txt file for
  full terms and conditions

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  THE SOFTWARE.

*/

var generate = require('oref0/lib/profile/');
function usage ( ) {
        console.log('usage: ', process.argv.slice(0, 2), '<pump_settings.json> <bg_targets.json> <insulin_sensitivities.json> <basal_profile.json> [<preferences.json>] [<carb_ratios.json>] [<temptargets.json>] [--model model.json] [--autotune autotune.json]');
}

function exportDefaults () {
	var defaults = generate.displayedDefaults();
	console.log(JSON.stringify(defaults, null, '\t'));
}

function updatePreferences (prefs) {
	var defaults = generate.displayedDefaults();

	// check for any displayedDefaults missing from current prefs and add from defaults

    for (var pref in defaults) {
      if (defaults.hasOwnProperty(pref) && !prefs.hasOwnProperty(pref)) {
        prefs[pref] = defaults[pref];
      }
    }

	console.log(JSON.stringify(prefs, null, '\t'));
}

if (!module.parent) {

    var argv = require('yargs')
      .usage("$0 pump_settings.json bg_targets.json insulin_sensitivities.json basal_profile.json [preferences.json] [<carb_ratios.json>] [<temptargets.json>] [--model model.json] [--autotune autotune.json]")
      .option('model', {
        alias: 'm',
        describe: "Pump model response",
        default: false
      })
      .option('autotune', {
        alias: 'a',
        describe: "Autotuned profile.json",
        default: false
      })
      .strict(true)
      .help('help')
      .option('exportDefaults', {
        describe: "Show typically-adjusted default preference values",
        default: false
      })
      .option('updatePreferences', {
        describe: "Check for any keys missing from current prefs and add from defaults",
        default: false
      })

    var params = argv.argv;
    var pumpsettings_input = params._[0]
    if ([null, '--help', '-h', 'help'].indexOf(pumpsettings_input) > 0) {
      usage( );
      process.exit(0);
    }
    if (params.exportDefaults) {
        exportDefaults();
        process.exit(0);
    }
    if (params.updatePreferences) {
        var preferences = {};
        var cwd = process.cwd()
        preferences = require(cwd + '/' + params.updatePreferences);
        updatePreferences(preferences);
        process.exit(0);
    }

    var bgtargets_input = params._[1]
    var isf_input = params._[2]
    var basalprofile_input = params._[3]
    var preferences_input = params._[4]
    var carbratio_input = params._[5]
    var temptargets_input = params._[6]
    var model_input = params.model;
    var autotune_input = params.autotune;

    if (!pumpsettings_input || !bgtargets_input || !isf_input || !basalprofile_input) {
        usage( );
        process.exit(1);
    }

    var cwd = process.cwd()
    var pumpsettings_data = require(cwd + '/' + pumpsettings_input);
    var bgtargets_data = require(cwd + '/' + bgtargets_input);
    if (bgtargets_data.units !== 'mg/dL') {
        if (bgtargets_data.units == 'mmol/L') {
            for (var i = 0, len = bgtargets_data.targets.length; i < len; i++) {
                bgtargets_data.targets[i].high = bgtargets_data.targets[i].high * 18;
                bgtargets_data.targets[i].low = bgtargets_data.targets[i].low * 18;
            }
            bgtargets_data.units = 'mg/dL';
        } else {
            console.log('BG Target data is expected to be expressed in mg/dL or mmol/L.'
                 , 'Found', bgtargets_data.units, 'in', bgtargets_input, '.');
            process.exit(2);
        }
    }

    var isf_data = require(cwd + '/' + isf_input);
    if (isf_data.units !== 'mg/dL') {
        if (isf_data.units == 'mmol/L') {
            for (var i = 0, len = isf_data.sensitivities.length; i < len; i++) {
                isf_data.sensitivities[i].sensitivity = isf_data.sensitivities[i].sensitivity * 18;
            }
            isf_data.units = 'mg/dL';
        } else {
            console.log('ISF is expected to be expressed in mg/dL or mmol/L.'
                    , 'Found', isf_data.units, 'in', isf_input, '.');
            process.exit(2);
        }
    }
    var basalprofile_data = require(cwd + '/' + basalprofile_input);

    var preferences = {};
    if (typeof preferences_input != 'undefined') {
        preferences = require(cwd + '/' + preferences_input);
    }
    var fs = require('fs');

    var model_data = { }
    if (params.model) {
      try {
        model_string = fs.readFileSync(model_input, 'utf8');
        model_data = model_string.replace(/\"/gi, '');
      } catch (e) {
        var msg = { error: e, msg: "Could not parse model_data", file: model_input};
        console.error(msg.msg);
        console.log(JSON.stringify(msg));
        process.exit(1);
      }
    }
    var autotune_data = { }
    if (params.autotune) {
      try {
        autotune_data = JSON.parse(fs.readFileSync(autotune_input, 'utf8'));

      } catch (e) {
        var msg = { error: e, msg: "Could not parse autotune_data", file: autotune_input};
        console.error(msg.msg);
        // Continue and output a non-autotuned profile if we don't have autotune_data
        //console.log(JSON.stringify(msg));
        //process.exit(1);
      }
    }

    var carbratio_data = { };
    //console.log("carbratio_input",carbratio_input);
    if (typeof carbratio_input != 'undefined') {
        try {
            carbratio_data = JSON.parse(fs.readFileSync(carbratio_input, 'utf8'));

        } catch (e) {
            var msg = { error: e, msg: "Could not parse carbratio_data. Feature Meal Assist enabled but cannot find required carb_ratios.", file: carbratio_input };
            console.error(msg.msg);
            console.log(JSON.stringify(msg));
            process.exit(1);
        }
        var errors = [ ];

        if (!(carbratio_data.schedule && carbratio_data.schedule[0].start && carbratio_data.schedule[0].ratio)) {
          errors.push({msg: "Carb ratio data should have an array called schedule with a start and ratio fields.", file: carbratio_input, data: carbratio_data});
        } else {
        }
        if (carbratio_data.units != 'grams' && carbratio_data.units != 'exchanges')  {
          errors.push({msg: "Carb ratio should have units field set to 'grams' or 'exchanges'.", file: carbratio_input, data: carbratio_data});
        }
        if (errors.length) {

          errors.forEach(function (msg) {
            console.error(msg.msg);
          });
          console.log(JSON.stringify(errors));
          process.exit(1);
        }
    }
    var temptargets_data = { };
    if (typeof temptargets_input != 'undefined') {
        try {
            temptargets_data = JSON.parse(fs.readFileSync(temptargets_input, 'utf8'));
        } catch (e) {
            console.error("Could not parse temptargets_data.");
        }
    }

    //console.log(carbratio_data);
    var inputs = { };

    //add all preferences to the inputs
    for (var pref in preferences) {
      if (preferences.hasOwnProperty(pref)) {
        inputs[pref] = preferences[pref];
      }
    }

    //make sure max_iob is set or default to 0
    inputs.max_iob = inputs.max_iob || 0;

    //set these after to make sure nothing happens if they are also set in preferences
    inputs.settings = pumpsettings_data;
    inputs.targets = bgtargets_data;
    inputs.basals = basalprofile_data;
    inputs.isf = isf_data;
    inputs.carbratio = carbratio_data;
    inputs.temptargets = temptargets_data;
    inputs.model = model_data;
    inputs.autotune = autotune_data;

    if (autotune_data) {
        if (autotune_data.basalprofile) { inputs.basals = autotune_data.basalprofile; }
        if (autotune_data.isfProfile) { inputs.isf = autotune_data.isfProfile; }
        if (autotune_data.carb_ratio) { inputs.carbratio.schedule[0].ratio = autotune_data.carb_ratio; }
    }
    var profile = generate(inputs);

    console.log(JSON.stringify(profile));

}
<|endoftext|>


<commit_before>"use string";

// var html=require('js-to-html').html;
var html=jsToHtml.html;

function prepareTableButtons(){
    var buttons = document.querySelectorAll("button#tables");
    Array.prototype.forEach.call(buttons, function(button){
        button.addEventListener('click', function(){
            var layout = document.getElementById('table_layout');
            var tableName = this.textContent;
            layout.textContent = 'loading...';
            var structureRequest = AjaxBestPromise.post({
                url:'table/structure',
                data:{
                    table:tableName
                }
            }).then(JSON.parse).then(function(tableDef){
                console.log(tableDef);
                var table = html.table([html.thead([html.tr(
                    tableDef.fields.map(function(fieldDef){
                        return html.th(fieldDef.title);
                    })
                )])]).create();
                layout.innerHTML='';
                layout.appendChild(table);
            });
            AjaxBestPromise.post({
                url:'table/data',
                data:{
                    table:tableName
                }
            }).then(function(result){
                structureRequest.then(function(){
                    layout.appendChild(html.pre('data '+result+' ----\n ').create());
                });
            });
        });
    });
}

window.addEventListener('load', function(){
    prepareTableButtons();
});<commit_msg>Agregamos los datos.<commit_after>"use string";

// var html=require('js-to-html').html;
var html=jsToHtml.html;

function prepareTableButtons(){
    var buttons = document.querySelectorAll("button#tables");
    Array.prototype.forEach.call(buttons, function(button){
        button.addEventListener('click', function(){
            var layout = document.getElementById('table_layout');
            var tableName = this.textContent;
            layout.textContent = 'loading...';
            var structureRequest = AjaxBestPromise.post({
                url:'table/structure',
                data:{
                    table:tableName
                }
            }).then(JSON.parse).then(function(tableDef){
                console.log(tableDef);
                var tableElement = html.table([
                    html.caption(tableDef.title),
                    html.thead([html.tr(
                        tableDef.fields.map(function(fieldDef){
                            return html.th(fieldDef.title);
                        })
                    )]),
                    html.tbody()
                ]).create();
                layout.innerHTML='';
                layout.appendChild(tableElement);
                return {element: tableElement, def: tableDef};
            });
            AjaxBestPromise.post({
                url:'table/data',
                data:{
                    table:tableName
                }
            }).then(JSON.parse).then(function(rows){
                structureRequest.then(function(table){
                    var tbody = table.element.tBodies[0];
                    rows.forEach(function(row){
                        var tr = tbody.insertRow(-1);
                        table.def.fields.forEach(function(fieldDef){
                            tr.appendChild(html.td([row[fieldDef.name]]).create());
                        });
                    });
                });
            });
        });
    });
}

window.addEventListener('load', function(){
    prepareTableButtons();
});<|endoftext|>


<commit_before>supervisor -w .,bin,config,models,public,routes,views app.js
<commit_msg>correct an error in the supervisor script<commit_after>supervisor -w .,bin,config,models,public,routes,views bin/www
<|endoftext|>


<commit_before>var CHOICE_MADE = 'choice_made';

var MealPicker = {
   selection: {},
   cacheDOM: function () {
      this.container = document.getElementById('meal-picker');
      this.submit = this.container.getElementsByTagName('button')[0];
   },
   bindUI: function () {
      var self = this;
      this.submit.addEventListener('click', function (ev) {
         ev.preventDefault();
         self.selection = [
            document.getElementsByName('food')[0].value,
            document.getElementsByName('drink')[0].value,
         ];

         Coordinator.broadcast(CHOICE_MADE, self.selection);
      });
   },
   init: function () {
      this.cacheDOM();
      this.bindUI();
   }
};

var Calculator = {
   menu: {
      sandwitch: { price: 8 },
      panini: { price: 7.5 },
      coffee: { price: 2 },
      juice: { price: 3 }
   },
   cacheDOM: function () {
      this.container = document.getElementById('calculator');
      this.elTotal = this.container.querySelector('[data-total]');
   },
   computeTotal: function (order) {
      var total = 0;
      for ( var i = 0; i < order.length; i++ ) {
         total += this.menu[order[i]]['price'];
      }
      return total;
   },
   printTotal: function (order) {
      this.elTotal.innerHTML = this.computeTotal(order);
   },
   init: function () {
      this.cacheDOM();
   }
};

var Printer = {
   menu: {
      sandwitch: { price: 8, printerName: 'SANDWITCH' },
      panini: { price: 7.5, printerName: 'PANINI' },
      coffee: { price: 2, printerName: 'COFFEE' },
      juice: { price: 3, printerName: 'JUICE' }
   },
   cacheDOM: function () {
      this.container = document.getElementById('summary');
      this.elSummary = this.container.querySelector('[data-summary]');
   },
   init: function () {
      this.cacheDOM();
   },
   generateSummary: function (order) {
      var html = '=====================================
';
      for ( var i = 0; i < order.length; i++ ) {
         html += '
' + this.menu[order[i]]['printerName'] + ' $' + this.menu[order[i]]['price'];
      }
      html += '
=====================================';
      return html;
   },
   printSummary: function (order) {
      this.elSummary.innerHTML = this.generateSummary(order);
   }
};

MealPicker.init();
Calculator.init();
Printer.init();

Coordinator.subscribe(CHOICE_MADE, Calculator.printTotal, Calculator);
Coordinator.subscribe(CHOICE_MADE, Printer.printSummary, Printer);

<commit_msg>Refactored browser example JS.<commit_after>var CHOICE_MADE = 'choice_made';

var Menu = {
   sandwitch: { price: 8, printerName: 'SANDWITCH' },
   panini: { price: 7.5, printerName: 'PANINI' },
   coffee: { price: 2, printerName: 'COFFEE' },
   juice: { price: 3, printerName: 'JUICE' }
};

var MealPicker = {
   selection: {},
   cacheDOM: function () {
      this.container = document.getElementById('meal-picker');
      this.submit = this.container.getElementsByTagName('button')[0];
   },
   bindUI: function () {
      var self = this;
      this.submit.addEventListener('click', function (ev) {
         ev.preventDefault();
         self.selection = [
            document.getElementsByName('food')[0].value,
            document.getElementsByName('drink')[0].value,
         ];

         Coordinator.broadcast(CHOICE_MADE, self.selection);
      });
   },
   init: function () {
      this.cacheDOM();
      this.bindUI();
   }
};

var Calculator = {
   cacheDOM: function () {
      this.container = document.getElementById('calculator');
      this.elTotal = this.container.querySelector('[data-total]');
   },
   computeTotal: function (order) {
      var total = 0;
      for ( var i = 0; i < order.length; i++ ) {
         total += Menu[order[i]]['price'];
      }
      return total;
   },
   printTotal: function (order) {
      this.elTotal.innerHTML = this.computeTotal(order);
   },
   init: function () {
      this.cacheDOM();
   }
};

var Printer = {
   cacheDOM: function () {
      this.container = document.getElementById('summary');
      this.elSummary = this.container.querySelector('[data-summary]');
   },
   init: function () {
      this.cacheDOM();
   },
   generateSummary: function (order) {
      var html = '=====================================
';
      for ( var i = 0; i < order.length; i++ ) {
         html += '
' + Menu[order[i]]['printerName'] + ' $' + Menu[order[i]]['price'];
      }
      html += '
=====================================';
      return html;
   },
   printSummary: function (order) {
      this.elSummary.innerHTML = this.generateSummary(order);
   }
};

MealPicker.init();
Calculator.init();
Printer.init();

Coordinator.subscribe(CHOICE_MADE, Calculator.printTotal, Calculator);
Coordinator.subscribe(CHOICE_MADE, Printer.printSummary, Printer);

<|endoftext|>


<commit_before>// Copyright (c) 2013 by John S. Ryan.  All rights reserved.  See LICENSE.txt for details.
"use strict";
var http = require("http");
var url = require("url");
var fs = require("fs");

var DEFAULT_404_HTML = "404 Not Found.";
//var DEFAULT_FILE_ROOT_DIRECTORY = fs.realpathSync(__dirname + "/../../../build/test");
var fileRootDirectory /*= DEFAULT_FILE_ROOT_DIRECTORY */;

function writeFile(response, filename, done) {
  fs.readFile(filename, function(err, data) {
    if (!err) {
      response.write(data);
    } else {
      response.statusCode = 500;
    }
    done();
  });
}

function fileSystemPathForUrl(urlPathname) {
  var fileExtension = ".html";
  return fileRootDirectory + "/" + urlPathname + fileExtension;
}

function serveFileForUrl(response, urlPath) {
  var filePath = fileSystemPathForUrl(urlPath);
  fs.exists(filePath, function(exists) {
    if (exists) {
      writeFile(response, filePath, function() {
        response.end();
      });
    } else {
      // base-case for recursive call: we're already trying to serve 404 and can't find THAT file.
      if(response.statusCode === 404) {
        response.write(DEFAULT_404_HTML);
        response.end();
      } else {
        response.statusCode = 404;
        serveFileForUrl(response, "404");
      }
    }
  });

}

// port = which TCP/IP port to listen on.
// rootDirectory = filesystem path to the root of what files this server should serve. (optional)
// callback = invoked when server is ready to accept connections.  (optional)
exports.start = function(port, rootDirectory, callback) {
  if (!port) { throw new Error("'port' is not optional."); }
  if (rootDirectory instanceof Function) {
    callback = rootDirectory;
    rootDirectory = undefined;
  }

  if (rootDirectory) {
    fileRootDirectory = rootDirectory;
  }

  var server = http.createServer();

  server.on("request", function(request, response) {
    var requestUrl = url.parse(request.url);
    serveFileForUrl(response, requestUrl.pathname);
  });
  server.listen(port, callback);

  var wrapper = (function() {
    return {
      "rootDirectory": fileRootDirectory,
      "stop": function(callback) {
        server.close(callback);
      }
    };
  })();

  return wrapper;
};
<commit_msg>Remove unnecessary default root directory path.<commit_after>// Copyright (c) 2013 by John S. Ryan.  All rights reserved.  See LICENSE.txt for details.
"use strict";
var http = require("http");
var url = require("url");
var fs = require("fs");

var DEFAULT_404_HTML = "404 Not Found.";
var fileRootDirectory;

function writeFile(response, filename, done) {
  fs.readFile(filename, function(err, data) {
    if (!err) {
      response.write(data);
    } else {
      response.statusCode = 500;
    }
    done();
  });
}

function fileSystemPathForUrl(urlPathname) {
  var fileExtension = ".html";
  return fileRootDirectory + "/" + urlPathname + fileExtension;
}

function serveFileForUrl(response, urlPath) {
  var filePath = fileSystemPathForUrl(urlPath);
  fs.exists(filePath, function(exists) {
    if (exists) {
      writeFile(response, filePath, function() {
        response.end();
      });
    } else {
      // base-case for recursive call: we're already trying to serve 404 and can't find THAT file.
      if(response.statusCode === 404) {
        response.write(DEFAULT_404_HTML);
        response.end();
      } else {
        response.statusCode = 404;
        serveFileForUrl(response, "404");
      }
    }
  });

}

// port = which TCP/IP port to listen on.
// rootDirectory = filesystem path to the root of what files this server should serve. (optional)
// callback = invoked when server is ready to accept connections.  (optional)
exports.start = function(port, rootDirectory, callback) {
  if (!port) { throw new Error("'port' is not optional."); }
  if (rootDirectory instanceof Function) {
    callback = rootDirectory;
    rootDirectory = undefined;
  }

  if (rootDirectory) {
    fileRootDirectory = rootDirectory;
  }

  var server = http.createServer();

  server.on("request", function(request, response) {
    var requestUrl = url.parse(request.url);
    serveFileForUrl(response, requestUrl.pathname);
  });
  server.listen(port, callback);

  var wrapper = (function() {
    return {
      "rootDirectory": fileRootDirectory,
      "stop": function(callback) {
        server.close(callback);
      }
    };
  })();

  return wrapper;
};
<|endoftext|>


<commit_before>// Copyright 2002-2014, University of Colorado Boulder

/**
 * Conductivity tester. Light bulb connected to a battery, with draggable probes.
 * When the probes are both immersed in solution, the circuit is completed, and the bulb glows.
 * This node assumes that it is located at (0,0), and its components are positioned in the world coordinate frame.
 *
 * @author Andrey Zelenkov (Mlearner)
 * @author Chris Malley (PixelZoom, Inc.)
 */
define( function( require ) {
  'use strict';

  // modules
  var Circle = require( 'SCENERY/nodes/Circle' );
  var Dimension2 = require( 'DOT/Dimension2' );
  var Image = require( 'SCENERY/nodes/Image' );
  var inherit = require( 'PHET_CORE/inherit' );
  var LightBulbNode = require( 'SCENERY_PHET/LightBulbNode' );
  var MinusNode = require( 'SCENERY_PHET/MinusNode' );
  var Node = require( 'SCENERY/nodes/Node' );
  var Path = require( 'SCENERY/nodes/Path' );
  var PhetFont = require( 'SCENERY_PHET/PhetFont' );
  var PlusNode = require( 'SCENERY_PHET/PlusNode' );
  var Property = require( 'AXON/Property' );
  var Rectangle = require( 'SCENERY/nodes/Rectangle' );
  var Shape = require( 'KITE/Shape' );
  var SimpleDragHandler = require( 'SCENERY/input/SimpleDragHandler' );
  var Text = require( 'SCENERY/nodes/Text' );
  var Util = require( 'DOT/Util' );
  var Vector2 = require( 'DOT/Vector2' );

  // images
  var batteryImage = require( 'image!ACID_BASE_SOLUTIONS/battery.png' );

  // strings
  var shortCircuitString = require( 'string!SCENERY_PHET/shortCircuit' );

  // constants
  var SHOW_TESTER_ORIGIN = false; // draws a red circle at the tester's origin, for debugging
  var SHOW_PROBE_ORIGIN = false; // draws a red circle at the origin of probes, for debugging

  /**
   * @param {Property.<number>} brightnessProperty brightness of bulb varies from 0 (off) to 1 (full on)
   * @param {Property.<Vector2>} bulbLocationProperty location of the bulb's tip
   * @param {Property.<Vector2>} positiveProbeLocationProperty location of bottom-center of the positive probe
   * @param {Property.<Vector2>} negativeProbeLocationProperty location of bottom-center of the negative probe
   * @param {Object} [options]
   * @constructor
   */
  function ConductivityTesterNode( brightnessProperty, bulbLocationProperty, positiveProbeLocationProperty, negativeProbeLocationProperty, options ) {

    options = _.extend( {
      // common to both probes
      probeSize: new Dimension2( 20, 68 ),
      probeLineWidth: 0.5,
      probeDragYRange: null, // {DOT.Range} no constraint on vertical dragging
      probeCursor: 'pointer',
      // positive probe
      positiveProbeFill: 'red',
      positiveProbeStroke: 'black',
      positiveLabelFill: 'white',
      // negative probe
      negativeProbeFill: 'black',
      negativeProbeStroke: 'black',
      negativeLabelFill: 'white',
      // wires
      wireStroke: 'black',
      wireLineWidth: 1.5,
      // miscellaneous
      bulbToBatteryWireLength: 40, // length of the wire between bulb and battery
      shortCircuitFont: new PhetFont( 14 ),
      shortCircuitFill: 'black'
    }, options );

    // @private bulb, origin at bottom center of base
    this.lightBulbNode = new LightBulbNode( brightnessProperty, {
      centerX: 0,
      bottom: 0
    } );

    // @private short-circuit indicator, centered above the light bulb
    this.shortCircuitNode = new Text( shortCircuitString, {
      font: options.shortCircuitFont,
      fill: options.shortCircuitFill,
      centerX: this.lightBulbNode.centerX,
      bottom: this.lightBulbNode.top,
      visible: false // initial state is no short circuit
    } );

    // battery
    var battery = new Image( batteryImage, {
      scale: 0.6,
      x: options.bulbToBatteryWireLength,
      centerY: 0
    } );

    // wire from bulb base to battery
    var bulbBatteryWire = new Path( new Shape().moveTo( 0, 0 ).lineTo( options.bulbToBatteryWireLength, 0 ), {
      stroke: options.wireStroke,
      lineWidth: options.wireLineWidth
    } );

    // apparatus (bulb + battery), origin at tip of bulb's base
    var apparatusNode = new Node( {
      translation: bulbLocationProperty.get(),
      children: [
        bulbBatteryWire,
        battery,
        this.lightBulbNode,
        this.shortCircuitNode
      ]
    } );
    if ( SHOW_TESTER_ORIGIN ) {
      apparatusNode.addChild( new Circle( 2, { fill: 'red' } ) );
    }

    // wire from battery terminal to positive probe
    var positiveWire = new WireNode(
      battery.getGlobalBounds().right,
      battery.getGlobalBounds().centerY,
      positiveProbeLocationProperty.get().x,
      positiveProbeLocationProperty.get().y - options.probeSize.height,
      { stroke: options.wireStroke, lineWidth: options.wireLineWidth }
    );

    // wire from base of bulb (origin) to negative probe
    var negativeWire = new WireNode(
      bulbLocationProperty.get().x - 5,
      bulbLocationProperty.get().y - 10,
      negativeProbeLocationProperty.get().x,
      negativeProbeLocationProperty.get().y - options.probeSize.height,
      { stroke: options.wireStroke, lineWidth: options.wireLineWidth }
    );

    // drag handler for probes
    var probeDragHandler = new SimpleDragHandler( {

      clickYOffset: 0,

      start: function( e ) {
        this.clickYOffset = e.currentTarget.globalToParentPoint( e.pointer.point ).y - e.currentTarget.y;
      },

      // probes move together
      drag: function( e ) {
        var y = e.currentTarget.globalToParentPoint( e.pointer.point ).y - this.clickYOffset;
        if ( options.probeDragYRange ) {
          y = Util.clamp( y, options.probeDragYRange.min, options.probeDragYRange.max );
        }
        positiveProbeLocationProperty.set( new Vector2( positiveProbeLocationProperty.get().x, y ) );
        negativeProbeLocationProperty.set( new Vector2( negativeProbeLocationProperty.get().x, y ) );
      }
    } );

    // probes
    var positiveProbe = new ProbeNode( probeDragHandler, new PlusNode( { fill: options.positiveLabelFill } ), {
      size: options.probeSize,
      fill: options.positiveProbeFill,
      stroke: options.positiveProbeStroke,
      lineWidth: options.probeLineWidth,
      cursor: options.probeCursor
    } );
    var negativeProbe = new ProbeNode( probeDragHandler, new MinusNode( { fill: options.negativeLabelFill } ), {
      size: options.probeSize,
      fill: options.negativeProbeFill,
      stroke: options.negativeProbeStroke,
      lineWidth: options.probeLineWidth,
      cursor: options.probeCursor
    } );

    Node.call( this, { children: [ positiveWire, negativeWire, positiveProbe, negativeProbe, apparatusNode ] } );

    // @private update positive wire if end point was changed
    this.positiveProbeObserver = function( positiveProbeLocation ) {
      positiveProbe.x = positiveProbeLocation.x;
      positiveProbe.y = positiveProbeLocation.y;
      positiveWire.setEndPoint( positiveProbeLocation.x, positiveProbeLocation.y - options.probeSize.height );
    };
    this.positivePropertyLocationProperty = positiveProbeLocationProperty; // @private
    this.positivePropertyLocationProperty.link( this.positiveProbeObserver );

    // @private update negative wire if end point was changed
    this.negativeProbeObserver = function( negativeProbeLocation ) {
      negativeProbe.x = negativeProbeLocation.x;
      negativeProbe.y = negativeProbeLocation.y;
      negativeWire.setEndPoint( negativeProbeLocation.x, negativeProbeLocation.y - options.probeSize.height );
    };
    this.negativePropertyLocationProperty = negativeProbeLocationProperty; // @private
    this.negativePropertyLocationProperty.link( this.negativeProbeObserver );
  }

  inherit( Node, ConductivityTesterNode, {

    // Ensures that this object is eligible for GC
    dispose: function() {

      // unlink from axon properties
      this.positivePropertyLocationProperty.unlink( this.positiveProbeObserver );
      this.negativePropertyLocationProperty.unlink( this.negativeProbeObserver );

      // dispose of sub-components
      this.lightBulbNode.dispose();
      this.lightBulbNode = null;
    },

    // @override
    setVisible: function( visible ) {
      Node.prototype.setVisible.call( this, visible );
      this.lightBulbNode.visible = visible; // to prevent light from updating when invisible
    },

    /**
     * Determines whether 'Short circuit' is shown above the light bulb. Note that it is the client's responsibility
     * to ensure that the bulb's brightness (as set by brightnessProperty) is appropriate for a short circuit.
     * @param {boolean} value
     */
    set shortCircuit( value ) { this.shortCircuitNode.visible = value; },

    /**
     * Is 'Short circuit' shown above the light bulb?
     * @returns {boolean}
     */
    get shortCircuit() { return this.shortCircuitNode.visible; }
  } );

  /**
   * Conductivity probe.
   *
   * @param {SimpleDragHandler} probeDragHandler
   * @param {Node} labelNode
   * @param {Object} [options]
   * @constructor
   */
  function ProbeNode( probeDragHandler, labelNode, options ) {

    options = _.extend( {
      size: new Dimension2( 20, 60 ),
      fill: 'white',
      stroke: 'black',
      lineWidth: 1.5,
      cursor: 'pointer'
    }, options );

    Node.call( this );

    // plate
    var plateNode = new Rectangle( -options.size.width / 2, -options.size.height, options.size.width, options.size.height, {
      fill: options.fill,
      stroke: options.stroke,
      lineWidth: options.lineWidth
    } );

    // scale the label to fix, place it towards bottom center
    labelNode.setScaleMagnitude( 0.5 * options.size.width / labelNode.width );
    labelNode.centerX = plateNode.centerX;
    labelNode.bottom = plateNode.bottom - 10;

    // rendering order
    this.addChild( plateNode );
    this.addChild( labelNode );
    if ( SHOW_PROBE_ORIGIN ) {
      this.addChild( new Circle( 2, { fill: 'red' } ) );
    }

    // expand touch area
    this.touchArea = this.localBounds.dilatedXY( 10, 10 );

    // interactivity
    this.addInputListener( probeDragHandler );

    this.mutate( options );
  }

  inherit( Node, ProbeNode );

  /**
   * Wires that connect to the probes.
   *
   * @param startX
   * @param startY
   * @param endX
   * @param endY
   * @param {Object} [options]
   * @constructor
   */
  function WireNode( startX, startY, endX, endY, options ) {

    Path.call( this );

    this.startPoint = { x: startX, y: startY }; // @private
    // control point offsets for when probe is to left of light bulb
    this.controlPointOffset = { x: 30, y: -50 }; // @private
    if ( endX < startX ) {
      // probe is to right of light bulb, flip sign on control point x-offset
      this.controlPointOffset.x = -this.controlPointOffset.x;
    }

    this.setEndPoint( endX, endY );

    this.mutate( options );
  }

  inherit( Path, WireNode, {

    // Sets the end point coordinates, the point attached to the probe.
    setEndPoint: function( endX, endY ) {

      var startX = this.startPoint.x;
      var startY = this.startPoint.y;
      var controlPointXOffset = this.controlPointOffset.x;
      var controlPointYOffset = this.controlPointOffset.y;

      this.setShape( new Shape()
          .moveTo( startX, startY )
          .cubicCurveTo( startX + controlPointXOffset, startY, endX, endY + controlPointYOffset, endX, endY )
      );
    }
  } );

  return ConductivityTesterNode;
} );
<commit_msg>doc options, #132<commit_after>// Copyright 2002-2014, University of Colorado Boulder

/**
 * Conductivity tester. Light bulb connected to a battery, with draggable probes.
 * When the probes are both immersed in solution, the circuit is completed, and the bulb glows.
 * This node assumes that it is located at (0,0), and its components are positioned in the world coordinate frame.
 *
 * @author Andrey Zelenkov (Mlearner)
 * @author Chris Malley (PixelZoom, Inc.)
 */
define( function( require ) {
  'use strict';

  // modules
  var Circle = require( 'SCENERY/nodes/Circle' );
  var Dimension2 = require( 'DOT/Dimension2' );
  var Image = require( 'SCENERY/nodes/Image' );
  var inherit = require( 'PHET_CORE/inherit' );
  var LightBulbNode = require( 'SCENERY_PHET/LightBulbNode' );
  var MinusNode = require( 'SCENERY_PHET/MinusNode' );
  var Node = require( 'SCENERY/nodes/Node' );
  var Path = require( 'SCENERY/nodes/Path' );
  var PhetFont = require( 'SCENERY_PHET/PhetFont' );
  var PlusNode = require( 'SCENERY_PHET/PlusNode' );
  var Property = require( 'AXON/Property' );
  var Rectangle = require( 'SCENERY/nodes/Rectangle' );
  var Shape = require( 'KITE/Shape' );
  var SimpleDragHandler = require( 'SCENERY/input/SimpleDragHandler' );
  var Text = require( 'SCENERY/nodes/Text' );
  var Util = require( 'DOT/Util' );
  var Vector2 = require( 'DOT/Vector2' );

  // images
  var batteryImage = require( 'image!ACID_BASE_SOLUTIONS/battery.png' );

  // strings
  var shortCircuitString = require( 'string!SCENERY_PHET/shortCircuit' );

  // constants
  var SHOW_TESTER_ORIGIN = false; // draws a red circle at the tester's origin, for debugging
  var SHOW_PROBE_ORIGIN = false; // draws a red circle at the origin of probes, for debugging

  /**
   * @param {Property.<number>} brightnessProperty brightness of bulb varies from 0 (off) to 1 (full on)
   * @param {Property.<Vector2>} bulbLocationProperty location of the bulb's tip
   * @param {Property.<Vector2>} positiveProbeLocationProperty location of bottom-center of the positive probe
   * @param {Property.<Vector2>} negativeProbeLocationProperty location of bottom-center of the negative probe
   * @param {Object} [options]
   * @constructor
   */
  function ConductivityTesterNode( brightnessProperty, bulbLocationProperty, positiveProbeLocationProperty, negativeProbeLocationProperty, options ) {

    options = _.extend( {
      // common to both probes
      probeSize: new Dimension2( 20, 68 ),
      probeLineWidth: 0.5,
      probeDragYRange: null, // {DOT.Range} no constraint on vertical dragging
      probeCursor: 'pointer',
      // positive probe
      positiveProbeFill: 'red',
      positiveProbeStroke: 'black',
      positiveLabelFill: 'white',
      // negative probe
      negativeProbeFill: 'black',
      negativeProbeStroke: 'black',
      negativeLabelFill: 'white',
      // wires
      wireStroke: 'black',
      wireLineWidth: 1.5,
      // short-circuit indicator
      shortCircuitFont: new PhetFont( 14 ),
      shortCircuitFill: 'black',
      // miscellaneous
      bulbToBatteryWireLength: 40 // length of the wire between bulb and battery
    }, options );

    // @private bulb, origin at bottom center of base
    this.lightBulbNode = new LightBulbNode( brightnessProperty, {
      centerX: 0,
      bottom: 0
    } );

    // @private short-circuit indicator, centered above the light bulb
    this.shortCircuitNode = new Text( shortCircuitString, {
      font: options.shortCircuitFont,
      fill: options.shortCircuitFill,
      centerX: this.lightBulbNode.centerX,
      bottom: this.lightBulbNode.top,
      visible: false // initial state is no short circuit
    } );

    // battery
    var battery = new Image( batteryImage, {
      scale: 0.6,
      x: options.bulbToBatteryWireLength,
      centerY: 0
    } );

    // wire from bulb base to battery
    var bulbBatteryWire = new Path( new Shape().moveTo( 0, 0 ).lineTo( options.bulbToBatteryWireLength, 0 ), {
      stroke: options.wireStroke,
      lineWidth: options.wireLineWidth
    } );

    // apparatus (bulb + battery), origin at tip of bulb's base
    var apparatusNode = new Node( {
      translation: bulbLocationProperty.get(),
      children: [
        bulbBatteryWire,
        battery,
        this.lightBulbNode,
        this.shortCircuitNode
      ]
    } );
    if ( SHOW_TESTER_ORIGIN ) {
      apparatusNode.addChild( new Circle( 2, { fill: 'red' } ) );
    }

    // wire from battery terminal to positive probe
    var positiveWire = new WireNode(
      battery.getGlobalBounds().right,
      battery.getGlobalBounds().centerY,
      positiveProbeLocationProperty.get().x,
      positiveProbeLocationProperty.get().y - options.probeSize.height,
      { stroke: options.wireStroke, lineWidth: options.wireLineWidth }
    );

    // wire from base of bulb (origin) to negative probe
    var negativeWire = new WireNode(
      bulbLocationProperty.get().x - 5,
      bulbLocationProperty.get().y - 10,
      negativeProbeLocationProperty.get().x,
      negativeProbeLocationProperty.get().y - options.probeSize.height,
      { stroke: options.wireStroke, lineWidth: options.wireLineWidth }
    );

    // drag handler for probes
    var probeDragHandler = new SimpleDragHandler( {

      clickYOffset: 0,

      start: function( e ) {
        this.clickYOffset = e.currentTarget.globalToParentPoint( e.pointer.point ).y - e.currentTarget.y;
      },

      // probes move together
      drag: function( e ) {
        var y = e.currentTarget.globalToParentPoint( e.pointer.point ).y - this.clickYOffset;
        if ( options.probeDragYRange ) {
          y = Util.clamp( y, options.probeDragYRange.min, options.probeDragYRange.max );
        }
        positiveProbeLocationProperty.set( new Vector2( positiveProbeLocationProperty.get().x, y ) );
        negativeProbeLocationProperty.set( new Vector2( negativeProbeLocationProperty.get().x, y ) );
      }
    } );

    // probes
    var positiveProbe = new ProbeNode( probeDragHandler, new PlusNode( { fill: options.positiveLabelFill } ), {
      size: options.probeSize,
      fill: options.positiveProbeFill,
      stroke: options.positiveProbeStroke,
      lineWidth: options.probeLineWidth,
      cursor: options.probeCursor
    } );
    var negativeProbe = new ProbeNode( probeDragHandler, new MinusNode( { fill: options.negativeLabelFill } ), {
      size: options.probeSize,
      fill: options.negativeProbeFill,
      stroke: options.negativeProbeStroke,
      lineWidth: options.probeLineWidth,
      cursor: options.probeCursor
    } );

    Node.call( this, { children: [ positiveWire, negativeWire, positiveProbe, negativeProbe, apparatusNode ] } );

    // @private update positive wire if end point was changed
    this.positiveProbeObserver = function( positiveProbeLocation ) {
      positiveProbe.x = positiveProbeLocation.x;
      positiveProbe.y = positiveProbeLocation.y;
      positiveWire.setEndPoint( positiveProbeLocation.x, positiveProbeLocation.y - options.probeSize.height );
    };
    this.positivePropertyLocationProperty = positiveProbeLocationProperty; // @private
    this.positivePropertyLocationProperty.link( this.positiveProbeObserver );

    // @private update negative wire if end point was changed
    this.negativeProbeObserver = function( negativeProbeLocation ) {
      negativeProbe.x = negativeProbeLocation.x;
      negativeProbe.y = negativeProbeLocation.y;
      negativeWire.setEndPoint( negativeProbeLocation.x, negativeProbeLocation.y - options.probeSize.height );
    };
    this.negativePropertyLocationProperty = negativeProbeLocationProperty; // @private
    this.negativePropertyLocationProperty.link( this.negativeProbeObserver );
  }

  inherit( Node, ConductivityTesterNode, {

    // Ensures that this object is eligible for GC
    dispose: function() {

      // unlink from axon properties
      this.positivePropertyLocationProperty.unlink( this.positiveProbeObserver );
      this.negativePropertyLocationProperty.unlink( this.negativeProbeObserver );

      // dispose of sub-components
      this.lightBulbNode.dispose();
      this.lightBulbNode = null;
    },

    // @override
    setVisible: function( visible ) {
      Node.prototype.setVisible.call( this, visible );
      this.lightBulbNode.visible = visible; // to prevent light from updating when invisible
    },

    /**
     * Determines whether 'Short circuit' is shown above the light bulb. Note that it is the client's responsibility
     * to ensure that the bulb's brightness (as set by brightnessProperty) is appropriate for a short circuit.
     * @param {boolean} value
     */
    set shortCircuit( value ) { this.shortCircuitNode.visible = value; },

    /**
     * Is 'Short circuit' shown above the light bulb?
     * @returns {boolean}
     */
    get shortCircuit() { return this.shortCircuitNode.visible; }
  } );

  /**
   * Conductivity probe.
   *
   * @param {SimpleDragHandler} probeDragHandler
   * @param {Node} labelNode
   * @param {Object} [options]
   * @constructor
   */
  function ProbeNode( probeDragHandler, labelNode, options ) {

    options = _.extend( {
      size: new Dimension2( 20, 60 ),
      fill: 'white',
      stroke: 'black',
      lineWidth: 1.5,
      cursor: 'pointer'
    }, options );

    Node.call( this );

    // plate
    var plateNode = new Rectangle( -options.size.width / 2, -options.size.height, options.size.width, options.size.height, {
      fill: options.fill,
      stroke: options.stroke,
      lineWidth: options.lineWidth
    } );

    // scale the label to fix, place it towards bottom center
    labelNode.setScaleMagnitude( 0.5 * options.size.width / labelNode.width );
    labelNode.centerX = plateNode.centerX;
    labelNode.bottom = plateNode.bottom - 10;

    // rendering order
    this.addChild( plateNode );
    this.addChild( labelNode );
    if ( SHOW_PROBE_ORIGIN ) {
      this.addChild( new Circle( 2, { fill: 'red' } ) );
    }

    // expand touch area
    this.touchArea = this.localBounds.dilatedXY( 10, 10 );

    // interactivity
    this.addInputListener( probeDragHandler );

    this.mutate( options );
  }

  inherit( Node, ProbeNode );

  /**
   * Wires that connect to the probes.
   *
   * @param startX
   * @param startY
   * @param endX
   * @param endY
   * @param {Object} [options]
   * @constructor
   */
  function WireNode( startX, startY, endX, endY, options ) {

    Path.call( this );

    this.startPoint = { x: startX, y: startY }; // @private
    // control point offsets for when probe is to left of light bulb
    this.controlPointOffset = { x: 30, y: -50 }; // @private
    if ( endX < startX ) {
      // probe is to right of light bulb, flip sign on control point x-offset
      this.controlPointOffset.x = -this.controlPointOffset.x;
    }

    this.setEndPoint( endX, endY );

    this.mutate( options );
  }

  inherit( Path, WireNode, {

    // Sets the end point coordinates, the point attached to the probe.
    setEndPoint: function( endX, endY ) {

      var startX = this.startPoint.x;
      var startY = this.startPoint.y;
      var controlPointXOffset = this.controlPointOffset.x;
      var controlPointYOffset = this.controlPointOffset.y;

      this.setShape( new Shape()
          .moveTo( startX, startY )
          .cubicCurveTo( startX + controlPointXOffset, startY, endX, endY + controlPointYOffset, endX, endY )
      );
    }
  } );

  return ConductivityTesterNode;
} );
<|endoftext|>


<commit_before>import SvgCanvas from "../../svgcanvas/svgcanvas.js";

const { $id, $qa } = SvgCanvas;

/*
 * register actions for left panel
 */
/**
 * @type {module}
 */
class LeftPanel {
  /**
   * @param {PlainObject} editor svgedit handler
   */
  constructor(editor) {
    this.editor = editor;
  }
  /**
   * This is a common function used when a tool has been clicked (chosen).
   * It does several common things:
   * - Removes the pressed button from whatever tool currently has it.
   * - Adds the the pressed button  to the button passed in.
   * @function this.updateLeftPanel
   * @param {string|Element} button The DOM element or string selector representing the toolbar button
   * @returns {boolean} Whether the button was disabled or not
   */
  // eslint-disable-next-line class-methods-use-this
  updateLeftPanel(button) {
    if (button.disabled) return false;
    // remove the pressed state on other(s) button(s)
    $qa("#tools_left *[pressed]").forEach((b) => {
      b.pressed = false;
    });
    // pressed state for the clicked button
    $id(button).pressed = true;
    return true;
  }

  /**
   * Unless the select toolbar button is disabled, sets the button
   * and sets the select mode and cursor styles.
   * @function module:SVGEditor.clickSelect
   * @returns {void}
   */
  clickSelect() {
    if (this.updateLeftPanel("tool_select")) {
      this.editor.workarea.style.cursor = "auto";
      this.editor.svgCanvas.setMode("select");
    }
  }

  /**
   *
   * @returns {void}
   */
  clickFHPath() {
    if (this.updateLeftPanel("tool_fhpath")) {
      this.editor.svgCanvas.setMode("fhpath");
    }
  }

  /**
   *
   * @returns {void}
   */
  clickLine() {
    if (this.updateLeftPanel("tool_line")) {
      this.editor.svgCanvas.setMode("line");
    }
  }

  /**
   *
   * @returns {void}
   */
  clickSquare() {
    if (this.updateLeftPanel("tool_square")) {
      this.editor.svgCanvas.setMode("square");
    }
  }

  /**
   *
   * @returns {void}
   */
  clickRect() {
    if (this.updateLeftPanel("tool_rect")) {
      this.editor.svgCanvas.setMode("rect");
    }
  }

  /**
   *
   * @returns {void}
   */
  clickFHRect() {
    if (this.updateLeftPanel("tool_fhrect")) {
      this.editor.svgCanvas.setMode("fhrect");
    }
  }

  /**
   *
   * @returns {void}
   */
  clickCircle() {
    if (this.updateLeftPanel("tool_circle")) {
      this.editor.svgCanvas.setMode("circle");
    }
  }

  /**
   *
   * @returns {void}
   */
  clickEllipse() {
    if (this.updateLeftPanel("tool_ellipse")) {
      this.editor.svgCanvas.setMode("ellipse");
    }
  }

  /**
   *
   * @returns {void}
   */
  clickFHEllipse() {
    if (this.updateLeftPanel("tool_fhellipse")) {
      this.editor.svgCanvas.setMode("fhellipse");
    }
  }

  /**
   *
   * @returns {void}
   */
  clickImage() {
    if (this.updateLeftPanel("tool_image")) {
      this.editor.svgCanvas.setMode("image");
    }
  }

  /**
   *
   * @returns {void}
   */
  clickZoom() {
    if (this.updateLeftPanel("tool_zoom")) {
      this.editor.svgCanvas.setMode("zoom");
      this.editor.workarea.style.cursor = this.editor.zoomInIcon;
    }
  }

  /**
   *
   * @returns {void}
   */
  dblclickZoom() {
    if (this.updateLeftPanel("tool_zoom")) {
      this.editor.zoomImage();
      this.clickSelect();
    }
  }

  /**
   *
   * @returns {void}
   */
  clickText() {
    if (this.updateLeftPanel("tool_text")) {
      this.editor.svgCanvas.setMode("text");
    }
  }

  /**
   *
   * @returns {void}
   */
  clickPath() {
    if (this.updateLeftPanel("tool_path")) {
      this.editor.svgCanvas.setMode("path");
    }
  }
  /**
   * @type {module}
   */
  add(id, handler) {
    $id(id).addEventListener("click", () => {
      if (this.updateLeftPanel(id)) {
        handler();
      }
    });
  }
  /**
   * @type {module}
   */
  init() {
    const { i18next } = this.editor
    // add Left panel
    const template = document.createElement("template");
    // eslint-disable-next-line no-unsanitized/property
    template.innerHTML = `

     <se-button id="tool_select" title="${i18next.t('tools.mode_select')}" src="./images/select.svg"></se-button>
     <se-button id="tool_zoom" title="${i18next.t('tools.mode_zoom')}" src="./images/zoom.svg" shortcut="Z"></se-button>
     <se-button id="tool_fhpath" title="${i18next.t('tools.mode_fhpath')}" src="./images/pencil.svg" shortcut="Q"></se-button>
     <se-button id="tool_line" title="${i18next.t('tools.mode_line')}" src="./images/pen.svg" shortcut="L"></se-button>
     <se-button id="tool_path" title="${i18next.t('tools.mode_path')}" src="./images/path.svg" shortcut="P"></se-button>
     <se-flyingbutton id="tools_rect" title="${i18next.t('tools.square_rect_tool')}">
       <se-button id="tool_rect" title="${i18next.t('tools.mode_rect')}" src="./images/rect.svg" shortcut="R"></se-button>
       <se-button id="tool_square" title="${i18next.t('tools.mode_square')}" src="./images/square.svg"></se-button>
       <se-button id="tool_fhrect" title="${i18next.t('tools.mode_fhrect')}" src="./images/fh_rect.svg"></se-button>
     </se-flyingbutton>
     <se-flyingbutton id="tools_ellipse" title="${i18next.t('tools.ellipse_circle_tool')}">
       <se-button id="tool_ellipse" title="${i18next.t('tools.mode_ellipse')}" src="./images/ellipse.svg" shortcut="E"></se-button>
       <se-button id="tool_circle" title="${i18next.t('tools.mode_circle')}" src="./images/circle.svg"></se-button>
       <se-button id="tool_fhellipse" title="${i18next.t('tools.mode_fhellipse')}" src="./images/fh_ellipse.svg"></se-button>
     </se-flyingbutton>
     <se-button id="tool_text" title="${i18next.t('tools.mode_text')}" src="./images/text.svg" shortcut="T"></se-button>
     <se-button id="tool_image" title="${i18next.t('tools.mode_image')}" src="./images/image.svg"></se-button>

     `;
    this.editor.$svgEditor.append(template.content.cloneNode(true));
    // register actions for left panel
    $id("tool_select").addEventListener("click", this.clickSelect.bind(this));
    $id("tool_fhpath").addEventListener("click", this.clickFHPath.bind(this));
    $id("tool_text").addEventListener("click", this.clickText.bind(this));
    $id("tool_image").addEventListener("click", this.clickImage.bind(this));
    $id("tool_zoom").addEventListener("click", this.clickZoom.bind(this));
    $id("tool_zoom").addEventListener("dblclick", this.dblclickZoom.bind(this));
    $id("tool_path").addEventListener("click", this.clickPath.bind(this));
    $id("tool_line").addEventListener("click", this.clickLine.bind(this));

    // flyout
    $id("tool_rect").addEventListener("click", this.clickRect.bind(this));
    $id("tool_square").addEventListener("click", this.clickSquare.bind(this));
    $id("tool_fhrect").addEventListener("click", this.clickFHRect.bind(this));
    $id("tool_ellipse").addEventListener("click", this.clickEllipse.bind(this));
    $id("tool_circle").addEventListener("click", this.clickCircle.bind(this));
    $id("tool_fhellipse").addEventListener(
      "click",
      this.clickFHEllipse.bind(this)
    );
  }
}

export default LeftPanel;
<commit_msg>fix linter issue<commit_after>import SvgCanvas from "../../svgcanvas/svgcanvas.js";

const { $id, $qa } = SvgCanvas;

/*
 * register actions for left panel
 */
/**
 * @type {module}
 */
class LeftPanel {
  /**
   * @param {PlainObject} editor svgedit handler
   */
  constructor(editor) {
    this.editor = editor;
  }
  /**
   * This is a common function used when a tool has been clicked (chosen).
   * It does several common things:
   * - Removes the pressed button from whatever tool currently has it.
   * - Adds the the pressed button  to the button passed in.
   * @function this.updateLeftPanel
   * @param {string|Element} button The DOM element or string selector representing the toolbar button
   * @returns {boolean} Whether the button was disabled or not
   */
  // eslint-disable-next-line class-methods-use-this
  updateLeftPanel(button) {
    if (button.disabled) return false;
    // remove the pressed state on other(s) button(s)
    $qa("#tools_left *[pressed]").forEach((b) => {
      b.pressed = false;
    });
    // pressed state for the clicked button
    $id(button).pressed = true;
    return true;
  }

  /**
   * Unless the select toolbar button is disabled, sets the button
   * and sets the select mode and cursor styles.
   * @function module:SVGEditor.clickSelect
   * @returns {void}
   */
  clickSelect() {
    if (this.updateLeftPanel("tool_select")) {
      this.editor.workarea.style.cursor = "auto";
      this.editor.svgCanvas.setMode("select");
    }
  }

  /**
   *
   * @returns {void}
   */
  clickFHPath() {
    if (this.updateLeftPanel("tool_fhpath")) {
      this.editor.svgCanvas.setMode("fhpath");
    }
  }

  /**
   *
   * @returns {void}
   */
  clickLine() {
    if (this.updateLeftPanel("tool_line")) {
      this.editor.svgCanvas.setMode("line");
    }
  }

  /**
   *
   * @returns {void}
   */
  clickSquare() {
    if (this.updateLeftPanel("tool_square")) {
      this.editor.svgCanvas.setMode("square");
    }
  }

  /**
   *
   * @returns {void}
   */
  clickRect() {
    if (this.updateLeftPanel("tool_rect")) {
      this.editor.svgCanvas.setMode("rect");
    }
  }

  /**
   *
   * @returns {void}
   */
  clickFHRect() {
    if (this.updateLeftPanel("tool_fhrect")) {
      this.editor.svgCanvas.setMode("fhrect");
    }
  }

  /**
   *
   * @returns {void}
   */
  clickCircle() {
    if (this.updateLeftPanel("tool_circle")) {
      this.editor.svgCanvas.setMode("circle");
    }
  }

  /**
   *
   * @returns {void}
   */
  clickEllipse() {
    if (this.updateLeftPanel("tool_ellipse")) {
      this.editor.svgCanvas.setMode("ellipse");
    }
  }

  /**
   *
   * @returns {void}
   */
  clickFHEllipse() {
    if (this.updateLeftPanel("tool_fhellipse")) {
      this.editor.svgCanvas.setMode("fhellipse");
    }
  }

  /**
   *
   * @returns {void}
   */
  clickImage() {
    if (this.updateLeftPanel("tool_image")) {
      this.editor.svgCanvas.setMode("image");
    }
  }

  /**
   *
   * @returns {void}
   */
  clickZoom() {
    if (this.updateLeftPanel("tool_zoom")) {
      this.editor.svgCanvas.setMode("zoom");
      this.editor.workarea.style.cursor = this.editor.zoomInIcon;
    }
  }

  /**
   *
   * @returns {void}
   */
  dblclickZoom() {
    if (this.updateLeftPanel("tool_zoom")) {
      this.editor.zoomImage();
      this.clickSelect();
    }
  }

  /**
   *
   * @returns {void}
   */
  clickText() {
    if (this.updateLeftPanel("tool_text")) {
      this.editor.svgCanvas.setMode("text");
    }
  }

  /**
   *
   * @returns {void}
   */
  clickPath() {
    if (this.updateLeftPanel("tool_path")) {
      this.editor.svgCanvas.setMode("path");
    }
  }
  /**
   * @type {module}
   */
  add(id, handler) {
    $id(id).addEventListener("click", () => {
      if (this.updateLeftPanel(id)) {
        handler();
      }
    });
  }
  /**
   * @type {module}
   */
  init() {
    const { i18next } = this.editor;
    // add Left panel
    const template = document.createElement("template");
    // eslint-disable-next-line no-unsanitized/property
    template.innerHTML = `

     <se-button id="tool_select" title="${i18next.t('tools.mode_select')}" src="./images/select.svg"></se-button>
     <se-button id="tool_zoom" title="${i18next.t('tools.mode_zoom')}" src="./images/zoom.svg" shortcut="Z"></se-button>
     <se-button id="tool_fhpath" title="${i18next.t('tools.mode_fhpath')}" src="./images/pencil.svg" shortcut="Q"></se-button>
     <se-button id="tool_line" title="${i18next.t('tools.mode_line')}" src="./images/pen.svg" shortcut="L"></se-button>
     <se-button id="tool_path" title="${i18next.t('tools.mode_path')}" src="./images/path.svg" shortcut="P"></se-button>
     <se-flyingbutton id="tools_rect" title="${i18next.t('tools.square_rect_tool')}">
       <se-button id="tool_rect" title="${i18next.t('tools.mode_rect')}" src="./images/rect.svg" shortcut="R"></se-button>
       <se-button id="tool_square" title="${i18next.t('tools.mode_square')}" src="./images/square.svg"></se-button>
       <se-button id="tool_fhrect" title="${i18next.t('tools.mode_fhrect')}" src="./images/fh_rect.svg"></se-button>
     </se-flyingbutton>
     <se-flyingbutton id="tools_ellipse" title="${i18next.t('tools.ellipse_circle_tool')}">
       <se-button id="tool_ellipse" title="${i18next.t('tools.mode_ellipse')}" src="./images/ellipse.svg" shortcut="E"></se-button>
       <se-button id="tool_circle" title="${i18next.t('tools.mode_circle')}" src="./images/circle.svg"></se-button>
       <se-button id="tool_fhellipse" title="${i18next.t('tools.mode_fhellipse')}" src="./images/fh_ellipse.svg"></se-button>
     </se-flyingbutton>
     <se-button id="tool_text" title="${i18next.t('tools.mode_text')}" src="./images/text.svg" shortcut="T"></se-button>
     <se-button id="tool_image" title="${i18next.t('tools.mode_image')}" src="./images/image.svg"></se-button>

     `;
    this.editor.$svgEditor.append(template.content.cloneNode(true));
    // register actions for left panel
    $id("tool_select").addEventListener("click", this.clickSelect.bind(this));
    $id("tool_fhpath").addEventListener("click", this.clickFHPath.bind(this));
    $id("tool_text").addEventListener("click", this.clickText.bind(this));
    $id("tool_image").addEventListener("click", this.clickImage.bind(this));
    $id("tool_zoom").addEventListener("click", this.clickZoom.bind(this));
    $id("tool_zoom").addEventListener("dblclick", this.dblclickZoom.bind(this));
    $id("tool_path").addEventListener("click", this.clickPath.bind(this));
    $id("tool_line").addEventListener("click", this.clickLine.bind(this));

    // flyout
    $id("tool_rect").addEventListener("click", this.clickRect.bind(this));
    $id("tool_square").addEventListener("click", this.clickSquare.bind(this));
    $id("tool_fhrect").addEventListener("click", this.clickFHRect.bind(this));
    $id("tool_ellipse").addEventListener("click", this.clickEllipse.bind(this));
    $id("tool_circle").addEventListener("click", this.clickCircle.bind(this));
    $id("tool_fhellipse").addEventListener(
      "click",
      this.clickFHEllipse.bind(this)
    );
  }
}

export default LeftPanel;
<|endoftext|>


<commit_before>/*

hashban.js is a plugin to streamline the addition of ajax-style page transitions
to a website.

Example:

    $.hashban.setup({
        contentWrapSelector: '#mycontent'
    });
    $('#header nav a').hashban();

Options/defaults:

    contentWrapSelector: '#content',
    duration: 500,
    transition_out: transition_out,
    transition_in: transition_in,
    loader: $.hashban.loader,
    link_order: [],
    link_filter: null,
    loaderTimeout: 300

*/

var Hashban = (function($) {

    function Hashban(user_options) {
        if (!(this instanceof Hashban)) return new Hashban(user_options);
        var that = this;

        function transition_out(endfade, old_content, direction, url) {
            // Default transition out: fade the old content out, and call
            // end_callback once the transition is done

            old_content.fadeOut(that.options.duration, endfade);
        };

        function transition_in(new_content, old_content, direction, contentBody) {
            // Default transition in: remove the old content (in case it's
            // still there) and fade the new content in.

            old_content.remove();
            new_content.fadeIn(that.options.duration);
        };

        function loader(visible) {
            // this function always returns the loader element,
            // creating it if necessary, and additionally shows
            // or hides the loader if the visible parameter is
            // exactly true or false

            // TODO uid for loader class?

            var loading = $('.hashban-loader');
            if (!loading.length) {
                loading = $('<span>').addClass('hashban-loader').text('Loading').hide();
                $(that.options.contentWrapSelector).append(loading);
            }
            if (visible === true) {
                loading.show();
            }
            else if (visible === false) {
                loading.hide();
            }
            return loading;
        };

        var default_options = {
            contentWrapSelector: '#content',
            duration: 500,
            transition_out: transition_out,
            transition_in: transition_in,
            content_init: null,
            loader: loader,
            link_order: [],
            loaderTimeout: 300,
            uid: 'Hashban.js'
        };

        this.CACHE = {};
        this.currentXHR = null;
        this.previous_url = window.location.pathname;
        this.options = $.extend(default_options, user_options);

    };

    Hashban.prototype.bind = function(){
        var that = this;

        $(window).bind('popstate', function(e) {
            if (e.originalEvent.state &&
                e.originalEvent.state['handler'] === that.options['uid']) {
                that.loadPage(window.location.pathname);
            }
        });
    };

    Hashban.prototype.hashban = function(links){
        // bind the given links (a jquery collection) to load via ajax

        var that = this;
        links.each(function() {
            var me = $(this),
                url = me.attr('href');

            // TODO uid here? can you bind two Hashbans to one link?

            if (!me.data('hashbanned')) {
                me.click(function() {
                    that.loadPage(url, true);
                    return false;
                });
                me.data('hashbanned', true);
            }
        });

    };

    Hashban.prototype.hijack = function(el, link_filter) {
        // find suitable links within the given element(s), and hijack them
        // to load via ajax

        var domain = getmatch(window.location.href, /[^\/]+\/\/[^\/]+/, 0),
            links = el.find('a:not([href^="http://"]), a[href^="' + domain + '"]')
                        .not('[href$=".xml"],[href$=".pdf"], [href$=".jpg"], [href$=".gif"], [href$=".png"], [href^="#"]');

        if (link_filter) {
            links = links.filter(link_filter);
        }

        this.hashban(links);

        return el;
    };

    Hashban.prototype.loadPage = function (url, push_state) {
        // Load the specified url, optionally changing the page state

        var that = this,
            contentWrap = $(this.options.contentWrapSelector),
            html,
            error,
            loaderTimer,
            faded = false,
            completed = false,
            direction = get_direction(this.previous_url, url, this.options.link_order);

        if (this.previous_url !== url) {
            this.previous_url = url;

            // Order of events
            // 1. start loading
            // 2. transition out old content
            // 3. show spinner
            // 4. once load and transition out are both done, transition in

            function fadein() {
                var contentBody = Hashban.getBody(html),
                    bodyClass = contentBody.attr('class'),
                    contentEl = contentBody.find(that.options.contentWrapSelector),
                    title = getmatch(html, /<title>(.*?)<\/title>/, 1);

                if (contentEl.length) {
                    if (push_state) {
                        history.pushState({
                            handler: that.options['uid']
                        }, null, url);
                    }
                    document.title = $('<div>').html(title).text();

                    $('body').attr('class', bodyClass);

                    clearTimeout(loaderTimer);
                    that.options.loader(false);

                    var new_content = contentEl.children(),
                        old_content = contentWrap.children().not(that.options.loader());

                    // trigger unload event here so that it won't effect any new content
                    // TODO uid here?
                    $(window).trigger('hashban-unload');

                    new_content.appendTo(contentWrap).hide();

                    that.options.transition_in(new_content, old_content, direction, contentBody);

                    if (typeof that.options.content_init === 'function') {
                        that.options.content_init(new_content);
                    }

                    // trigger load event here - assume that new content has been added in transition_in
                    // TODO uid here?
                    $(window).trigger('hashban-load');

                    // work around jquery's auto overflow switch - see http://goo.gl/V9UUw
                    contentWrap.css({
                        overflow: 'visible'
                    });

                }
                else {
                    // reload the page to show the error
                    window.location.href = url;
                }

            };

            function endfade() {
                if (!completed) {
                    completed = true;
                    if (html) {
                        fadein();
                    }
                    else {
                        loaderTimer = setTimeout(function() {
                            that.options.loader(true);
                        }, that.options.loaderTimeout);
                        faded = true;
                    }
                }
            };

            var old_content = $(this.options.contentWrapSelector).children().not(this.options.loader());

            if (this.CACHE[url]) {
                html = this.CACHE[url];
            }
            else {
                if (this.currentXHR) {
                    this.currentXHR.abort();
                }
                this.currentXHR = $.ajax(url, {
                    success: function(data) {
                        that.CACHE[url] = data;
                        html = data;
                        if (faded) {
                            fadein();
                        }
                    },
                    error: function(jqXHR, textStatus, errorThrown) {
                        html = jqXHR.responseText;
                        error = errorThrown;
                        if (faded) {
                            fadein();
                        }
                    }
                });
            }

            if (old_content.length && typeof this.options.transition_out === 'function') {
                this.options.transition_out(endfade, old_content, direction, url);
            }
            else {
                endfade();
            }

        }
    };

    // PUBLIC INSTANCE METHODS

    Hashban.prototype.bindUntilUnload = function (el, event, callback) {
        // bind event to el, and unbind when navigating away from the page

        // TODO - account for uid here

        el.on(event, callback);
        $(window).one('hashban-unload', function() {
            el.off(event, callback);
        });
    };

    Hashban.prototype.preload = function (url) {
        // preload a url to avoid transition delay
        var that = this;

        if (!this.CACHE[url]) {
            $.ajax(url, {
                success: function(data) {
                    that.CACHE[url] = data;
                }
            });
        }
    };

    // PUBLIC UTILITIES

    Hashban.getBody = function (html) {
        // get wrapped body element from an html document

        return $('<div' + getmatch(html, /<body([^>]*>[\S\s]*)<\/body>/, 1) + '</div>');
    };

    // INTERNAL UTILITIES

    function getmatch(str, re, i) {
        // find and return the ith matched pattern in a regex, or
        // return a blank string if not found

        var match = str.match(re);
        if (match) {
            return match[i];
        }
        else {
            return '';
        }
    };

    function get_direction(from, to, link_order) {
        // Returns 1 (forwards) or -1 (backwards) for a given transition.
        // 0 is returned when the direction cannot be inferred.
        // link_order should be a list of the site's top-level link urls,
        // in order (see setup function).

        if (!link_order) {
            link_order = [];
        }

        if (to === from) {
            // this shouldn't really happen
            return 0;
        }
        else if (is_sublink(from, to)) {
            // if to is a child of from, go forward
            return 1;
        }
        else if (is_sublink(to, from)) {
            // if from is a child of to, go backwards
            return -1;
        }
        else {
            // otherwise, investigate the link_order setting to see
            // which way to go:
            var from_i = -1,
                to_i = -1;

            // first look for exact matches
            for (var i = 0; i < link_order.length; i++) {
                if (from_i === -1 && from === link_order[i]) {
                    from_i = i;
                }
                if (to_i === -1 && to === link_order[i]) {
                    to_i = i;
                }
            }
            if (from_i === -1 || to_i === -1) {
                // if no exact match, look for parent urls that match
                for (var i = 0; i < link_order.length; i++) {
                    if (from_i === -1 && is_sublink(link_order[i], from)) {
                        from_i = i;
                    }
                    if (to_i === -1 && is_sublink(link_order[i], to)) {
                        to_i = i;
                    }
                }
            }

            if (from_i === to_i || from_i === -1 || to_i === -1) {
                // can't determine a direction
                return 0;
            }
            else if (to_i < from_i) {
                // return backwards
                return -1;
            }
            else {
                // return forwards
                return 1;
            }

        }
    };

    function is_sublink(link, possible_sublink) {
        // Determine whether possible_sublink is a child of
        // link in the url tree. Returns false if the links
        // are the same.

        // add trailing slashes if they're missing, to ensure
        // that is_sublink(/test', '/test-2') returns false,
        // but is_sublink(/test', '/test/2') returns true.
        if (possible_sublink.slice(-1) !== '/') {
            possible_sublink += '/';
        }
        if (link.slice(-1) !== '/') {
            link += '/';
        }

        if (link !== possible_sublink && possible_sublink.indexOf(link) === 0) {
            return true;
        }
        else {
            return false;
        }
    };

    return Hashban;

})(jQuery);
<commit_msg>Fixed out-of-date example in source<commit_after>/*

hashban.js is a plugin to streamline the addition of ajax-style page transitions
to a website.

Example:

    var hashban = Hashban({
        contentWrapSelector: '#mycontent'
    });
    hashban.hijack($('#header nav'));
    hashban.bind();

Options/defaults:

    contentWrapSelector: '#content',
    duration: 500,
    transition_out: transition_out,
    transition_in: transition_in,
    loader: $.hashban.loader,
    link_order: [],
    link_filter: null,
    loaderTimeout: 300

*/

var Hashban = (function($) {

    function Hashban(user_options) {
        if (!(this instanceof Hashban)) return new Hashban(user_options);
        var that = this;

        function transition_out(endfade, old_content, direction, url) {
            // Default transition out: fade the old content out, and call
            // end_callback once the transition is done

            old_content.fadeOut(that.options.duration, endfade);
        };

        function transition_in(new_content, old_content, direction, contentBody) {
            // Default transition in: remove the old content (in case it's
            // still there) and fade the new content in.

            old_content.remove();
            new_content.fadeIn(that.options.duration);
        };

        function loader(visible) {
            // this function always returns the loader element,
            // creating it if necessary, and additionally shows
            // or hides the loader if the visible parameter is
            // exactly true or false

            // TODO uid for loader class?

            var loading = $('.hashban-loader');
            if (!loading.length) {
                loading = $('<span>').addClass('hashban-loader').text('Loading').hide();
                $(that.options.contentWrapSelector).append(loading);
            }
            if (visible === true) {
                loading.show();
            }
            else if (visible === false) {
                loading.hide();
            }
            return loading;
        };

        var default_options = {
            contentWrapSelector: '#content',
            duration: 500,
            transition_out: transition_out,
            transition_in: transition_in,
            content_init: null,
            loader: loader,
            link_order: [],
            loaderTimeout: 300,
            uid: 'Hashban.js'
        };

        this.CACHE = {};
        this.currentXHR = null;
        this.previous_url = window.location.pathname;
        this.options = $.extend(default_options, user_options);

    };

    Hashban.prototype.bind = function(){
        var that = this;

        $(window).bind('popstate', function(e) {
            if (e.originalEvent.state &&
                e.originalEvent.state['handler'] === that.options['uid']) {
                that.loadPage(window.location.pathname);
            }
        });
    };

    Hashban.prototype.hashban = function(links){
        // bind the given links (a jquery collection) to load via ajax

        var that = this;
        links.each(function() {
            var me = $(this),
                url = me.attr('href');

            // TODO uid here? can you bind two Hashbans to one link?

            if (!me.data('hashbanned')) {
                me.click(function() {
                    that.loadPage(url, true);
                    return false;
                });
                me.data('hashbanned', true);
            }
        });

    };

    Hashban.prototype.hijack = function(el, link_filter) {
        // find suitable links within the given element(s), and hijack them
        // to load via ajax

        var domain = getmatch(window.location.href, /[^\/]+\/\/[^\/]+/, 0),
            links = el.find('a:not([href^="http://"]), a[href^="' + domain + '"]')
                        .not('[href$=".xml"],[href$=".pdf"], [href$=".jpg"], [href$=".gif"], [href$=".png"], [href^="#"]');

        if (link_filter) {
            links = links.filter(link_filter);
        }

        this.hashban(links);

        return el;
    };

    Hashban.prototype.loadPage = function (url, push_state) {
        // Load the specified url, optionally changing the page state

        var that = this,
            contentWrap = $(this.options.contentWrapSelector),
            html,
            error,
            loaderTimer,
            faded = false,
            completed = false,
            direction = get_direction(this.previous_url, url, this.options.link_order);

        if (this.previous_url !== url) {
            this.previous_url = url;

            // Order of events
            // 1. start loading
            // 2. transition out old content
            // 3. show spinner
            // 4. once load and transition out are both done, transition in

            function fadein() {
                var contentBody = Hashban.getBody(html),
                    bodyClass = contentBody.attr('class'),
                    contentEl = contentBody.find(that.options.contentWrapSelector),
                    title = getmatch(html, /<title>(.*?)<\/title>/, 1);

                if (contentEl.length) {
                    if (push_state) {
                        history.pushState({
                            handler: that.options['uid']
                        }, null, url);
                    }
                    document.title = $('<div>').html(title).text();

                    $('body').attr('class', bodyClass);

                    clearTimeout(loaderTimer);
                    that.options.loader(false);

                    var new_content = contentEl.children(),
                        old_content = contentWrap.children().not(that.options.loader());

                    // trigger unload event here so that it won't effect any new content
                    // TODO uid here?
                    $(window).trigger('hashban-unload');

                    new_content.appendTo(contentWrap).hide();

                    that.options.transition_in(new_content, old_content, direction, contentBody);

                    if (typeof that.options.content_init === 'function') {
                        that.options.content_init(new_content);
                    }

                    // trigger load event here - assume that new content has been added in transition_in
                    // TODO uid here?
                    $(window).trigger('hashban-load');

                    // work around jquery's auto overflow switch - see http://goo.gl/V9UUw
                    contentWrap.css({
                        overflow: 'visible'
                    });

                }
                else {
                    // reload the page to show the error
                    window.location.href = url;
                }

            };

            function endfade() {
                if (!completed) {
                    completed = true;
                    if (html) {
                        fadein();
                    }
                    else {
                        loaderTimer = setTimeout(function() {
                            that.options.loader(true);
                        }, that.options.loaderTimeout);
                        faded = true;
                    }
                }
            };

            var old_content = $(this.options.contentWrapSelector).children().not(this.options.loader());

            if (this.CACHE[url]) {
                html = this.CACHE[url];
            }
            else {
                if (this.currentXHR) {
                    this.currentXHR.abort();
                }
                this.currentXHR = $.ajax(url, {
                    success: function(data) {
                        that.CACHE[url] = data;
                        html = data;
                        if (faded) {
                            fadein();
                        }
                    },
                    error: function(jqXHR, textStatus, errorThrown) {
                        html = jqXHR.responseText;
                        error = errorThrown;
                        if (faded) {
                            fadein();
                        }
                    }
                });
            }

            if (old_content.length && typeof this.options.transition_out === 'function') {
                this.options.transition_out(endfade, old_content, direction, url);
            }
            else {
                endfade();
            }

        }
    };

    // PUBLIC INSTANCE METHODS

    Hashban.prototype.bindUntilUnload = function (el, event, callback) {
        // bind event to el, and unbind when navigating away from the page

        // TODO - account for uid here

        el.on(event, callback);
        $(window).one('hashban-unload', function() {
            el.off(event, callback);
        });
    };

    Hashban.prototype.preload = function (url) {
        // preload a url to avoid transition delay
        var that = this;

        if (!this.CACHE[url]) {
            $.ajax(url, {
                success: function(data) {
                    that.CACHE[url] = data;
                }
            });
        }
    };

    // PUBLIC UTILITIES

    Hashban.getBody = function (html) {
        // get wrapped body element from an html document

        return $('<div' + getmatch(html, /<body([^>]*>[\S\s]*)<\/body>/, 1) + '</div>');
    };

    // INTERNAL UTILITIES

    function getmatch(str, re, i) {
        // find and return the ith matched pattern in a regex, or
        // return a blank string if not found

        var match = str.match(re);
        if (match) {
            return match[i];
        }
        else {
            return '';
        }
    };

    function get_direction(from, to, link_order) {
        // Returns 1 (forwards) or -1 (backwards) for a given transition.
        // 0 is returned when the direction cannot be inferred.
        // link_order should be a list of the site's top-level link urls,
        // in order (see setup function).

        if (!link_order) {
            link_order = [];
        }

        if (to === from) {
            // this shouldn't really happen
            return 0;
        }
        else if (is_sublink(from, to)) {
            // if to is a child of from, go forward
            return 1;
        }
        else if (is_sublink(to, from)) {
            // if from is a child of to, go backwards
            return -1;
        }
        else {
            // otherwise, investigate the link_order setting to see
            // which way to go:
            var from_i = -1,
                to_i = -1;

            // first look for exact matches
            for (var i = 0; i < link_order.length; i++) {
                if (from_i === -1 && from === link_order[i]) {
                    from_i = i;
                }
                if (to_i === -1 && to === link_order[i]) {
                    to_i = i;
                }
            }
            if (from_i === -1 || to_i === -1) {
                // if no exact match, look for parent urls that match
                for (var i = 0; i < link_order.length; i++) {
                    if (from_i === -1 && is_sublink(link_order[i], from)) {
                        from_i = i;
                    }
                    if (to_i === -1 && is_sublink(link_order[i], to)) {
                        to_i = i;
                    }
                }
            }

            if (from_i === to_i || from_i === -1 || to_i === -1) {
                // can't determine a direction
                return 0;
            }
            else if (to_i < from_i) {
                // return backwards
                return -1;
            }
            else {
                // return forwards
                return 1;
            }

        }
    };

    function is_sublink(link, possible_sublink) {
        // Determine whether possible_sublink is a child of
        // link in the url tree. Returns false if the links
        // are the same.

        // add trailing slashes if they're missing, to ensure
        // that is_sublink(/test', '/test-2') returns false,
        // but is_sublink(/test', '/test/2') returns true.
        if (possible_sublink.slice(-1) !== '/') {
            possible_sublink += '/';
        }
        if (link.slice(-1) !== '/') {
            link += '/';
        }

        if (link !== possible_sublink && possible_sublink.indexOf(link) === 0) {
            return true;
        }
        else {
            return false;
        }
    };

    return Hashban;

})(jQuery);
<|endoftext|>