<commit_before>/*
 * This file is part of Arduino
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * 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.
 *
 * Copyright 2015 Arduino Srl (http://www.arduino.org/)
 *
 * authors: arduino.org team - support@arduino.org
 *
 */

(function () {
	"use strict";

	var child_process   = require('child_process'),
		timer			= require('timers'),
		os              = require('os'),
		path			= require('path');

	var domainName = "org-arduino-ide-domain-debug",
		dManager;

	var openOcdProcess,
		gdbProcess;

	var rootDir = __dirname.substring(0, __dirname.lastIndexOf(path.sep)),
		interval = 0;

	function getTmpFolder()
	{
		return os.tmpdir();
	}

	function getOpenOcd()
	{
		return rootDir+((process.platform =='win32')? '\\hardware\\tools\\OpenOCD-0.9.0-arduino\\bin\\openocd' : '/hardware/tools/OpenOCD-0.9.0-arduino/bin/openocd')
	}

	function getScriptDir()
	{
		return rootDir+((process.platform =='win32')? '\\hardware\\tools\\OpenOCD-0.9.0-arduino\\share\\openocd\\scripts' : '/hardware/tools/OpenOCD-0.9.0-arduino/share/openocd/scripts')
	}

	function getScript()
	{
		return rootDir+((process.platform =='win32')? '\\hardware\\arduino\\samd\\variants\\arduino_zero\\openocd_scripts\\arduino_zero.cfg' : '/hardware/arduino/samd/variants/arduino_zero/openocd_scripts/arduino_zero.cfg')
	}

	function getGdb()
	{
		return rootDir+((process.platform =='win32')? '\\hardware\\tools\\samd\\bin\\arm-none-eabi-gdb' : '/hardware/tools/samd/bin/arm-none-eabi-gdb')
	}

	function stopAll()
	{
		if(!openOcdProcess.killed)
		{
			if(process.platform == 'win32')
				process.kill(openOcdProcess.pid, 'SIGKILL');
			else
				process.kill(-openOcdProcess.pid)
		}
		if(!gdbProcess.killed)
			process.kill(gdbProcess.pid, 'SIGKILL');

	}

	function launchOpenOcd()
	{
		var OpenOcdCmd = [getOpenOcd(), "-s", getScriptDir(), "-f", getScript()];
		openOcdProcess = child_process.spawn(OpenOcdCmd[0], OpenOcdCmd.slice(1), {detached: true});

		openOcdProcess.on('close', function(code, signal){
			console.log("OPENOCD PROCESS KILLED");
			dManager.emitEvent(domainName, "close_flag", "1")
		});

		return openOcdProcess.pid;
	}

	function launchGdb(elfFile, sketchFolder)
	{
		var gdbCmd = [getGdb(), "-d", "'" + sketchFolder + "'"]
		gdbProcess = child_process.exec(gdbCmd[0], gdbCmd.slice(1));

		gdbProcess.stdout.on("data", function(data){
			dManager.emitEvent(domainName, "debug_data", data.toString());
		});

		gdbProcess.stderr.on("data", function(data){
			dManager.emitEvent(domainName, "debug_err", data.toString());
		});

		gdbProcess.on('close', function(code, signal){
			console.log("GDB PROCESS KILLED");
			dManager.emitEvent(domainName, "close_flag", "1")
		});

		if(gdbProcess.pid)
			timer.setTimeout(function(){
				locateElfFile(elfFile)
				timer.setTimeout(function(){
					locateLiveProgram()
				}, interval);
			}, interval);
	}

	function locateElfFile(filepath)
	{
		console.log("--|| Locate elf file ||--")
		if(process.platform == 'win32' || process.platform == 'linux') {
			gdbProcess.stdin.write("file " + filepath + " \n")
			console.log("file " + filepath + " \n")
		}
		else {
			gdbProcess.stdin.write("file " + filepath.substring(0, filepath.lastIndexOf(path.sep)) + " \n")
			console.log("file " + filepath.substring(0, filepath.lastIndexOf(path.sep)) + " \n")
		}

	}

	function locateLiveProgram()
	{
		console.log("--|| Locate live program ||--")
		gdbProcess.stdin.write("target remote localhost:3333"+" \n")
		console.log("target remote localhost:3333"+" \n")
		//dManager.emitEvent(domainName, "debug_data", "target remote localhost:3333"+" \n");
	}

	function stopExecution()
	{
		console.log("--|| Stop sketch ||--")
		gdbProcess.stdin.write(" monitor reset halt"+" \n")
		dManager.emitEvent(domainName, "debug_data", "Halt")
	}

	function restartExecution()
	{
		console.log("--|| Restart sketch ||--")
		gdbProcess.stdin.write(" monitor reset run"+" \n")
		dManager.emitEvent(domainName, "debug_data", "Resume")
	}

	function stepNextBp()
	{
		console.log("--|| Continue sketch execution ||--")
		gdbProcess.stdin.write(" continue" + " \n")
		//dManager.emitEvent(domainName, "debug_data", "continue")
	}

	function stepNextLine()
	{
		console.log("--|| Step Next Line ||--")
		gdbProcess.stdin.write(" next" + " \n")
		//dManager.emitEvent(domainName, "debug_data", "next")
	}

	function showBreakpoints()
	{
		console.log("--|| Show a list of breakpoints ||--")
		gdbProcess.stdin.write(" info breakpoints " + " \n")
		//dManager.emitEvent(domainName, "debug_data", "info breakpoints")
	}

	function setBreakpoint(filename, line)
	{
		console.log("--|| Set breakpoint at " + line + " ||--")
		gdbProcess.stdin.write(" break " + filename +  ":" + line + " \n")
		//dManager.emitEvent(domainName, "debug_data", "b " + line)
	}

	function showVariables()
	{
		console.log("--|| Show variables ||--")
		gdbProcess.stdin.write(" info locals" + " \n")
		//dManager.emitEvent(domainName, "debug_data", "print " + variable)
	}

	function saveBreakpoints(bpList, filename)
	{
		console.log("--|| Save breakpoint on file ||--")
		//gdbProcess.stdin.write("save breakpoints" + filename +" \n")
		//oppure
		fs.writeFile(filename, bpList, function(err){
			if(err)
				dManager.emitEvent(domainName, "debug_err", "Error in breakpoint file saving...");
			else
				dManager.emitEvent(domainName, "debug_data", "Breakpoint file saved");

		})
	}

	function deleteBreakpoint(filename, line)
	{
		console.log("--|| Delete breakpoint at " + line + " ||--")
		gdbProcess.stdin.write("clear " + filename +  ":" + line + " \n")
	}

	function init(domainManager){
		if(!domainManager.hasDomain( domainName )){
			domainManager.registerDomain( domainName, {major: 0, minor: 1});
		}
		dManager = domainManager;

//------------     COMMANDS     ------------
		//<editor-fold desc="launchOpenOcd">
		dManager.registerCommand(
			domainName,
			"launchOpenOcd",
			launchOpenOcd,
			false,
			"Start Open Ocd",
			[],
			[{	name:"OpenOcdPID",
				type:"string",
				description:"OpenOcd Process ID"
			}]
		);
		//</editor-fold>

		//<editor-fold desc="stopAll">
		dManager.registerCommand(
			domainName,
			"stopAll",
			stopAll,
			false,
			"Stop Debug"
		);
		//</editor-fold>

		//<editor-fold desc="halt">
		dManager.registerCommand(
			domainName,
			"halt",
			stopExecution,
			false,
			"Stop sketch execution"
		);
		//</editor-fold>

		//<editor-fold desc="restart">
		dManager.registerCommand(
			domainName,
			"restart",
			restartExecution,
			false,
			"Restart sketch execution"
		);
		//</editor-fold>

		//<editor-fold desc="launchGdb">
		dManager.registerCommand(
			domainName,
			"launchGdb",
			launchGdb,
			false,
			"Start Gdb",
			[{
				name:"elfFile",
				type:"string",
				description:"Elf file to locate"
			},
			{
				name:"sketchFolder",
				type:"string",
				description:"Sketch folder"
			}]
		);
		//</editor-fold>

		//<editor-fold desc="show_breakpoints">
		dManager.registerCommand(
			domainName,
			"show_breakpoints",
			showBreakpoints,
			false,
			"Show a list of breakpoints",
			[],
			[{	name:"breakpointList",
				type:"string",
				description:"List of breakpoints"
			}]
		);
		//</editor-fold>

		//<editor-fold desc="set_breakpoint">
		dManager.registerCommand(
			domainName,
			"set_breakpoint",
			setBreakpoint,
			false,
			"Set breakpoint",
			[{	name:"sketchname",
				type:"string",
				description:"File in witch set breakpoint"
			},
				{	name:"breakpointLine",
				type:"int",
				description:"Set a breakpoint at breakpointLine row"
			}]
		);
		//</editor-fold>

		//<editor-fold desc="deleteBreakpoint">
		dManager.registerCommand(
			domainName,
			"deleteBreakpoint",
			deleteBreakpoint,
			false,
			"Delete breakpoint",
			[{	name:"sketchname",
				type:"string",
				description:"File in witch delete breakpoint"
			},
				{	name:"breakpointLine",
					type:"int",
					description:"Delete the breakpoint at breakpointLine row"
				}]
		);
		//</editor-fold>

		//<editor-fold desc="saveBreakpoint">
		dManager.registerCommand(
			domainName,
			"saveBreakpoints",
			saveBreakpoints,
			false,
			"Save breakpoint in a file",
			[{	name:"bpList",
				type:"string",
				description:"List of breakpoints"
			},
			{	name:"filename",
				type:"string",
				description:"Name of file(absolute) to save breakpoints"
			}]
		);
		//</editor-fold>

		//<editor-fold desc="show_variables">
		dManager.registerCommand(
			domainName,
			"show_variables",
			showVariables,
			false,
			"Show variables with their values"
		);
		//</editor-fold>

		//<editor-fold desc="step_next_line">
		dManager.registerCommand(
			domainName,
			"step_next_line",
			stepNextLine,
			false,
			"Step next line"
		);
		//</editor-fold>

		//<editor-fold desc="step_next_bp">
		dManager.registerCommand(
			domainName,
			"step_next_bp",
			stepNextBp,
			false,
			"Step next line"
		);
		//</editor-fold>

		//<editor-fold desc="getTmpFolder">
		dManager.registerCommand(
			domainName,
			"getTmpFolder",
			getTmpFolder,
			false,
			"Get tmp folder"
		);
		//</editor-fold>

//------------     EVENTS     ------------
		//<editor-fold desc="debug_data">
		dManager.registerEvent(
			domainName,
			"debug_data",
			[{	name:"ddata",
				type:"string",
				description:"data from gdb"
			}]
		);
		//</editor-fold>

		//<editor-fold desc="debug_err">
		dManager.registerEvent(
			domainName,
			"debug_err",
			[{	name:"derr",
				type:"string",
				description:"error from gdb"
			}]
		);
		//</editor-fold>

		//<editor-fold desc="close_flag">
		dManager.registerEvent(
			domainName,
			"close_flag",
			[{	name:"flag",
				type:"int",
				description:"Communicates if openOcd and GDB were closed"
			}]
		);
		//</editor-fold>

	}

	exports.init = init;
}());<commit_msg>fix problem with elf file location<commit_after>/*
 * This file is part of Arduino
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * 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.
 *
 * Copyright 2015 Arduino Srl (http://www.arduino.org/)
 *
 * authors: arduino.org team - support@arduino.org
 *
 */

(function () {
	"use strict";

	var child_process   = require('child_process'),
		timer			= require('timers'),
		os              = require('os'),
		path			= require('path');

	var domainName = "org-arduino-ide-domain-debug",
		dManager;

	var openOcdProcess,
		gdbProcess;

	var rootDir = __dirname.substring(0, __dirname.lastIndexOf(path.sep)),
		interval = 0;

	function getTmpFolder()
	{
		return os.tmpdir();
	}

	function getOpenOcd()
	{
		return rootDir+((process.platform =='win32')? '\\hardware\\tools\\OpenOCD-0.9.0-arduino\\bin\\openocd' : '/hardware/tools/OpenOCD-0.9.0-arduino/bin/openocd')
	}

	function getScriptDir()
	{
		return rootDir+((process.platform =='win32')? '\\hardware\\tools\\OpenOCD-0.9.0-arduino\\share\\openocd\\scripts' : '/hardware/tools/OpenOCD-0.9.0-arduino/share/openocd/scripts')
	}

	function getScript()
	{
		return rootDir+((process.platform =='win32')? '\\hardware\\arduino\\samd\\variants\\arduino_zero\\openocd_scripts\\arduino_zero.cfg' : '/hardware/arduino/samd/variants/arduino_zero/openocd_scripts/arduino_zero.cfg')
	}

	function getGdb()
	{
		return rootDir+((process.platform =='win32')? '\\hardware\\tools\\samd\\bin\\arm-none-eabi-gdb' : '/hardware/tools/samd/bin/arm-none-eabi-gdb')
	}

	function stopAll()
	{
		if(!openOcdProcess.killed)
		{
			if(process.platform == 'win32')
				process.kill(openOcdProcess.pid, 'SIGKILL');
			else
				process.kill(-openOcdProcess.pid)
		}
		if(!gdbProcess.killed)
			process.kill(gdbProcess.pid, 'SIGKILL');

	}

	function launchOpenOcd()
	{
		var OpenOcdCmd = [getOpenOcd(), "-s", getScriptDir(), "-f", getScript()];
		openOcdProcess = child_process.spawn(OpenOcdCmd[0], OpenOcdCmd.slice(1), {detached: true});

		openOcdProcess.on('close', function(code, signal){
			console.log("OPENOCD PROCESS KILLED");
			dManager.emitEvent(domainName, "close_flag", "1")
		});

		return openOcdProcess.pid;
	}

	function launchGdb(elfFile, sketchFolder)
	{
		var gdbCmd = [getGdb(), "-d", "'" + sketchFolder + "'"]
		gdbProcess = child_process.exec(gdbCmd[0], gdbCmd.slice(1));

		gdbProcess.stdout.on("data", function(data){
			dManager.emitEvent(domainName, "debug_data", data.toString());
		});

		gdbProcess.stderr.on("data", function(data){
			dManager.emitEvent(domainName, "debug_err", data.toString());
		});

		gdbProcess.on('close', function(code, signal){
			console.log("GDB PROCESS KILLED");
			dManager.emitEvent(domainName, "close_flag", "1")
		});

		if(gdbProcess.pid)
			timer.setTimeout(function(){
				locateElfFile(elfFile)
				timer.setTimeout(function(){
					locateLiveProgram()
				}, interval);
			}, interval);
	}

	function locateElfFile(filepath)
	{
		console.log("--|| Locate elf file ||--")
		gdbProcess.stdin.write("file " + filepath + " \n")
		console.log("file " + filepath + " \n")
	}

	function locateLiveProgram()
	{
		console.log("--|| Locate live program ||--")
		gdbProcess.stdin.write("target remote localhost:3333"+" \n")
		console.log("target remote localhost:3333"+" \n")
		//dManager.emitEvent(domainName, "debug_data", "target remote localhost:3333"+" \n");
	}

	function stopExecution()
	{
		console.log("--|| Stop sketch ||--")
		gdbProcess.stdin.write(" monitor reset halt"+" \n")
		dManager.emitEvent(domainName, "debug_data", "Halt")
	}

	function restartExecution()
	{
		console.log("--|| Restart sketch ||--")
		gdbProcess.stdin.write(" monitor reset run"+" \n")
		dManager.emitEvent(domainName, "debug_data", "Resume")
	}

	function stepNextBp()
	{
		console.log("--|| Continue sketch execution ||--")
		gdbProcess.stdin.write(" continue" + " \n")
		//dManager.emitEvent(domainName, "debug_data", "continue")
	}

	function stepNextLine()
	{
		console.log("--|| Step Next Line ||--")
		gdbProcess.stdin.write(" next" + " \n")
		//dManager.emitEvent(domainName, "debug_data", "next")
	}

	function showBreakpoints()
	{
		console.log("--|| Show a list of breakpoints ||--")
		gdbProcess.stdin.write(" info breakpoints " + " \n")
		//dManager.emitEvent(domainName, "debug_data", "info breakpoints")
	}

	function setBreakpoint(filename, line)
	{
		console.log("--|| Set breakpoint at " + line + " ||--")
		gdbProcess.stdin.write(" break " + filename +  ":" + line + " \n")
		//dManager.emitEvent(domainName, "debug_data", "b " + line)
	}

	function showVariables()
	{
		console.log("--|| Show variables ||--")
		gdbProcess.stdin.write(" info locals" + " \n")
		//dManager.emitEvent(domainName, "debug_data", "print " + variable)
	}

	function saveBreakpoints(bpList, filename)
	{
		console.log("--|| Save breakpoint on file ||--")
		//gdbProcess.stdin.write("save breakpoints" + filename +" \n")
		//oppure
		fs.writeFile(filename, bpList, function(err){
			if(err)
				dManager.emitEvent(domainName, "debug_err", "Error in breakpoint file saving...");
			else
				dManager.emitEvent(domainName, "debug_data", "Breakpoint file saved");

		})
	}

	function deleteBreakpoint(filename, line)
	{
		console.log("--|| Delete breakpoint at " + line + " ||--")
		gdbProcess.stdin.write("clear " + filename +  ":" + line + " \n")
	}

	function init(domainManager){
		if(!domainManager.hasDomain( domainName )){
			domainManager.registerDomain( domainName, {major: 0, minor: 1});
		}
		dManager = domainManager;

//------------     COMMANDS     ------------
		//<editor-fold desc="launchOpenOcd">
		dManager.registerCommand(
			domainName,
			"launchOpenOcd",
			launchOpenOcd,
			false,
			"Start Open Ocd",
			[],
			[{	name:"OpenOcdPID",
				type:"string",
				description:"OpenOcd Process ID"
			}]
		);
		//</editor-fold>

		//<editor-fold desc="stopAll">
		dManager.registerCommand(
			domainName,
			"stopAll",
			stopAll,
			false,
			"Stop Debug"
		);
		//</editor-fold>

		//<editor-fold desc="halt">
		dManager.registerCommand(
			domainName,
			"halt",
			stopExecution,
			false,
			"Stop sketch execution"
		);
		//</editor-fold>

		//<editor-fold desc="restart">
		dManager.registerCommand(
			domainName,
			"restart",
			restartExecution,
			false,
			"Restart sketch execution"
		);
		//</editor-fold>

		//<editor-fold desc="launchGdb">
		dManager.registerCommand(
			domainName,
			"launchGdb",
			launchGdb,
			false,
			"Start Gdb",
			[{
				name:"elfFile",
				type:"string",
				description:"Elf file to locate"
			},
			{
				name:"sketchFolder",
				type:"string",
				description:"Sketch folder"
			}]
		);
		//</editor-fold>

		//<editor-fold desc="show_breakpoints">
		dManager.registerCommand(
			domainName,
			"show_breakpoints",
			showBreakpoints,
			false,
			"Show a list of breakpoints",
			[],
			[{	name:"breakpointList",
				type:"string",
				description:"List of breakpoints"
			}]
		);
		//</editor-fold>

		//<editor-fold desc="set_breakpoint">
		dManager.registerCommand(
			domainName,
			"set_breakpoint",
			setBreakpoint,
			false,
			"Set breakpoint",
			[{	name:"sketchname",
				type:"string",
				description:"File in witch set breakpoint"
			},
				{	name:"breakpointLine",
				type:"int",
				description:"Set a breakpoint at breakpointLine row"
			}]
		);
		//</editor-fold>

		//<editor-fold desc="deleteBreakpoint">
		dManager.registerCommand(
			domainName,
			"deleteBreakpoint",
			deleteBreakpoint,
			false,
			"Delete breakpoint",
			[{	name:"sketchname",
				type:"string",
				description:"File in witch delete breakpoint"
			},
				{	name:"breakpointLine",
					type:"int",
					description:"Delete the breakpoint at breakpointLine row"
				}]
		);
		//</editor-fold>

		//<editor-fold desc="saveBreakpoint">
		dManager.registerCommand(
			domainName,
			"saveBreakpoints",
			saveBreakpoints,
			false,
			"Save breakpoint in a file",
			[{	name:"bpList",
				type:"string",
				description:"List of breakpoints"
			},
			{	name:"filename",
				type:"string",
				description:"Name of file(absolute) to save breakpoints"
			}]
		);
		//</editor-fold>

		//<editor-fold desc="show_variables">
		dManager.registerCommand(
			domainName,
			"show_variables",
			showVariables,
			false,
			"Show variables with their values"
		);
		//</editor-fold>

		//<editor-fold desc="step_next_line">
		dManager.registerCommand(
			domainName,
			"step_next_line",
			stepNextLine,
			false,
			"Step next line"
		);
		//</editor-fold>

		//<editor-fold desc="step_next_bp">
		dManager.registerCommand(
			domainName,
			"step_next_bp",
			stepNextBp,
			false,
			"Step next line"
		);
		//</editor-fold>

		//<editor-fold desc="getTmpFolder">
		dManager.registerCommand(
			domainName,
			"getTmpFolder",
			getTmpFolder,
			false,
			"Get tmp folder"
		);
		//</editor-fold>

//------------     EVENTS     ------------
		//<editor-fold desc="debug_data">
		dManager.registerEvent(
			domainName,
			"debug_data",
			[{	name:"ddata",
				type:"string",
				description:"data from gdb"
			}]
		);
		//</editor-fold>

		//<editor-fold desc="debug_err">
		dManager.registerEvent(
			domainName,
			"debug_err",
			[{	name:"derr",
				type:"string",
				description:"error from gdb"
			}]
		);
		//</editor-fold>

		//<editor-fold desc="close_flag">
		dManager.registerEvent(
			domainName,
			"close_flag",
			[{	name:"flag",
				type:"int",
				description:"Communicates if openOcd and GDB were closed"
			}]
		);
		//</editor-fold>

	}

	exports.init = init;
}());<|endoftext|>


<commit_before>ManageIQ.angular.app.directive('formChanged', function() {
  return {
    require: 'form',
    link: function(scope, _elem, _attr, ctrl) {
      var model = function() {
        return scope.$eval(ctrl.model || scope.model);
      };

      var modelCopy = function() {
        return scope.$eval(ctrl.modelCopy || 'modelCopy');
      };

      var empty = function(object) {
        return object === undefined || object === '' || object === null;
      };

      var compare = function(original, copy, key) {
        // add missing keys in copy from original so recursion works
        if (_.isObject(copy) && _.isObject(original)) {
          _.difference(Object.keys(original), Object.keys(copy)).forEach(function(k) {
            copy[k] = undefined;
          });
        }
        // don't compare ng stuff TODO remove in Angular 2/4
        if (key !== undefined && key[0] === '$') {
          return true;
        }
        if (empty(original) && empty(copy)) {
          return true;
        }
        // use default compare
        return undefined;
      };

      scope.$watchCollection(ctrl.model || scope.model, function() {
        // TODO in lodash 4 it's _.isEqualWith
        if (_.isEqual(model(), modelCopy(), compare)) {
          ctrl.$setPristine();
        }
      });
    },
  };
});

<commit_msg>Explicitly set the form to dirty when models are mismatched<commit_after>ManageIQ.angular.app.directive('formChanged', function() {
  return {
    require: 'form',
    link: function(scope, _elem, _attr, ctrl) {
      var model = function() {
        return scope.$eval(ctrl.model || scope.model);
      };

      var modelCopy = function() {
        return scope.$eval(ctrl.modelCopy || 'modelCopy');
      };

      var empty = function(object) {
        return object === undefined || object === '' || object === null;
      };

      var compare = function(original, copy, key) {
        // add missing keys in copy from original so recursion works
        if (_.isObject(copy) && _.isObject(original)) {
          _.difference(Object.keys(original), Object.keys(copy)).forEach(function(k) {
            copy[k] = undefined;
          });
        }
        // don't compare ng stuff TODO remove in Angular 2/4
        if (key !== undefined && key[0] === '$') {
          return true;
        }
        if (empty(original) && empty(copy)) {
          return true;
        }
        // use default compare
        return undefined;
      };

      scope.$watchCollection(ctrl.model || scope.model, function() {
        // TODO in lodash 4 it's _.isEqualWith
        if (_.isEqual(model(), modelCopy(), compare)) {
          ctrl.$setPristine();
        } else {
          ctrl.$setDirty();
        }
      });
    },
  };
});

<|endoftext|>


<commit_before>/* global BreakString:true */

var updateRelativeAge, autoUpdatingRelativeAge, relativeAge, relativeAgeTiny,
    relativeAgeMedium, relativeAgeMediumSpan, longDate, longDateNoYear, toTitleCase,
    shortDate, shortDateNoYear, tinyDateYear, relativeAgeTinyShowsYear;

/*
* memoize.js
* by @philogb and @addyosmani
* with further optimizations by @mathias
* and @DmitryBaranovsk
* perf tests: http://bit.ly/q3zpG3
* Released under an MIT license.
*
* modified with cap by Sam
*/
var cappedMemoize = function ( fn, max ) {
    fn.maxMemoize = max;
    fn.memoizeLength = 0;

    return function () {
        var args = Array.prototype.slice.call(arguments),
            hash = "",
            i = args.length;
        var currentArg = null;
        while (i--) {
            currentArg = args[i];
            hash += (currentArg === new Object(currentArg)) ?
            JSON.stringify(currentArg) : currentArg;
            if(!fn.memoize) {
              fn.memoize = {};
            }
        }
        if (hash in fn.memoize) {
          return fn.memoize[hash];
        } else {
          fn.memoizeLength++;
          if(fn.memoizeLength > max) {
            fn.memoizeLength = 0;
            fn.memoize = {};
          }
          var result = fn.apply(this, args);
          fn.memoize[hash] = result;
          return result;
        }
    };
};

var breakUp = cappedMemoize(function(str, hint){
  return new BreakString(str).break(hint);
}, 100);

shortDate = function(date){
  return moment(date).format(I18n.t("dates.medium.date_year"));
};

shortDateNoYear = function(date) {
  return moment(date).format(I18n.t("dates.tiny.date_month"));
};

tinyDateYear = function(date) {
  return moment(date).format(I18n.t("dates.tiny.date_year"));
};

// http://stackoverflow.com/questions/196972/convert-string-to-title-case-with-javascript
// TODO: locale support ?
toTitleCase = function toTitleCase(str) {
  return str.replace(/\w\S*/g, function(txt){
    return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
  });
};

longDate = function(dt) {
  if (!dt) return;
  return moment(dt).longDate();
};

// suppress year, if current year
longDateNoYear = function(dt) {
  if (!dt) return;

  if ((new Date()).getFullYear() !== dt.getFullYear()) {
    return moment(dt).format(I18n.t("dates.long_date_with_year"));
  } else {
    return moment(dt).format(I18n.t("dates.long_date_without_year"));
  }
};

updateRelativeAge = function(elems) {
  // jQuery .each
  elems.each(function(){
    var $this = $(this);
    $this.html(relativeAge(new Date($this.data('time')), {format: $this.data('format'), wrapInSpan: false}));
  });
};

autoUpdatingRelativeAge = function(date,options) {
  if (!date) return "";
  if (+date === +new Date("1970/1/1 1:0:0")) return "";

  options = options || {};
  var format = options.format || "tiny";

  var append = "";

  if(format === 'medium') {
    append = " date";
    if(options.leaveAgo) {
      format = 'medium-with-ago';
    }
    options.wrapInSpan = false;
  }

  var relAge = relativeAge(date, options);

  if (format === 'tiny' && relativeAgeTinyShowsYear(relAge)) {
    append += " with-year";
  }

  if (options.title) {
    append += "' title='" + longDate(date);
  }

  return "" + relAge  + "";
};

relativeAgeTiny = function(date){
  var format = "tiny";
  var distance = Math.round((new Date() - date) / 1000);
  var distanceInMinutes = Math.round(distance / 60.0);

  var formatted;
  var t = function(key,opts){
    return I18n.t("dates." + format + "." + key, opts);
  };

  switch(true){

  case(distanceInMinutes < 1):
    formatted = t("less_than_x_minutes", {count: 1});
    break;
  case(distanceInMinutes >= 1 && distanceInMinutes <= 44):
    formatted = t("x_minutes", {count: distanceInMinutes});
    break;
  case(distanceInMinutes >= 45 && distanceInMinutes <= 89):
    formatted = t("about_x_hours", {count: 1});
    break;
  case(distanceInMinutes >= 90 && distanceInMinutes <= 1439):
    formatted = t("about_x_hours", {count: Math.round(distanceInMinutes / 60.0)});
    break;
  case(Discourse.SiteSettings.relative_date_duration === 0 && distanceInMinutes <= 525599):
    formatted = shortDateNoYear(date);
    break;
  case(distanceInMinutes >= 1440 && distanceInMinutes <= 2519):
    formatted = t("x_days", {count: 1});
    break;
  case(distanceInMinutes >= 2520 && distanceInMinutes <= ((Discourse.SiteSettings.relative_date_duration||14) * 1440)):
    formatted = t("x_days", {count: Math.round(distanceInMinutes / 1440.0)});
    break;
  default:
    if(date.getFullYear() === new Date().getFullYear()) {
      formatted = shortDateNoYear(date);
    } else {
      formatted = tinyDateYear(date);
    }
    break;
  }

  return formatted;
};

/*
 * Returns true if the given tiny date string includes the year.
 * Useful for checking if the string isn't so tiny.
 */
relativeAgeTinyShowsYear = function(relativeAgeString) {
  return relativeAgeString.match(/'[\d]{2}$/);
};

relativeAgeMediumSpan = function(distance, leaveAgo) {
  var formatted, distanceInMinutes;

  distanceInMinutes = Math.round(distance / 60.0);

  var t = function(key, opts){
    return I18n.t("dates.medium" + (leaveAgo?"_with_ago":"") + "." + key, opts);
  };

  switch(true){
  case(distanceInMinutes >= 1 && distanceInMinutes <= 56):
    formatted = t("x_minutes", {count: distanceInMinutes});
    break;
  case(distanceInMinutes >= 56 && distanceInMinutes <= 89):
    formatted = t("x_hours", {count: 1});
    break;
  case(distanceInMinutes >= 90 && distanceInMinutes <= 1379):
    formatted = t("x_hours", {count: Math.round(distanceInMinutes / 60.0)});
    break;
  case(distanceInMinutes >= 1380 && distanceInMinutes <= 2159):
    formatted = t("x_days", {count: 1});
    break;
  case(distanceInMinutes >= 2160):
    formatted = t("x_days", {count: Math.round((distanceInMinutes - 720.0) / 1440.0)});
    break;
  }
  return formatted || '&mdash';
};

relativeAgeMedium = function(date, options){
  var displayDate, fiveDaysAgo, oneMinuteAgo, fullReadable, leaveAgo;
  var wrapInSpan = options.wrapInSpan !== false;

  leaveAgo = options.leaveAgo;
  var distance = Math.round((new Date() - date) / 1000);

  if (!date) {
    return "—";
  }

  fullReadable = longDate(date);
  displayDate = "";
  fiveDaysAgo = 432000;
  oneMinuteAgo = 60;

  if (distance < oneMinuteAgo) {
    displayDate = I18n.t("now");
  } else if (distance > fiveDaysAgo) {
    if ((new Date()).getFullYear() !== date.getFullYear()) {
      displayDate = shortDate(date);
    } else {
      displayDate = shortDateNoYear(date);
    }
  } else {
    displayDate = relativeAgeMediumSpan(distance, leaveAgo);
  }
  if(wrapInSpan) {
    return "" + displayDate + "";
  } else {
    return displayDate;
  }
};

// mostly lifted from rails with a few amendments
relativeAge = function(date, options) {
  options = options || {};
  var format = options.format || "tiny";

  if(format === "tiny") {
    return relativeAgeTiny(date, options);
  } else if (format === "medium") {
    return relativeAgeMedium(date, options);
  } else if (format === 'medium-with-ago') {
    return relativeAgeMedium(date, _.extend(options, {format: 'medium', leaveAgo: true}));
  }

  return "UNKNOWN FORMAT";
};

var number = function(val) {
  val = parseInt(val, 10);
  if (isNaN(val)) val = 0;

  if (val > 999) {
    return (val / 1000).toFixed(1) + "K";
  }
  return val.toString();
};

Discourse.Formatter = {
  longDate: longDate,
  longDateNoYear: longDateNoYear,
  relativeAge: relativeAge,
  autoUpdatingRelativeAge: autoUpdatingRelativeAge,
  updateRelativeAge: updateRelativeAge,
  toTitleCase: toTitleCase,
  shortDate: shortDate,
  breakUp: breakUp,
  cappedMemoize: cappedMemoize,
  number: number
};
<commit_msg>FIX: damn timezones<commit_after>/* global BreakString:true */

var updateRelativeAge, autoUpdatingRelativeAge, relativeAge, relativeAgeTiny,
    relativeAgeMedium, relativeAgeMediumSpan, longDate, longDateNoYear, toTitleCase,
    shortDate, shortDateNoYear, tinyDateYear, relativeAgeTinyShowsYear;

/*
* memoize.js
* by @philogb and @addyosmani
* with further optimizations by @mathias
* and @DmitryBaranovsk
* perf tests: http://bit.ly/q3zpG3
* Released under an MIT license.
*
* modified with cap by Sam
*/
var cappedMemoize = function ( fn, max ) {
    fn.maxMemoize = max;
    fn.memoizeLength = 0;

    return function () {
        var args = Array.prototype.slice.call(arguments),
            hash = "",
            i = args.length;
        var currentArg = null;
        while (i--) {
            currentArg = args[i];
            hash += (currentArg === new Object(currentArg)) ?
            JSON.stringify(currentArg) : currentArg;
            if(!fn.memoize) {
              fn.memoize = {};
            }
        }
        if (hash in fn.memoize) {
          return fn.memoize[hash];
        } else {
          fn.memoizeLength++;
          if(fn.memoizeLength > max) {
            fn.memoizeLength = 0;
            fn.memoize = {};
          }
          var result = fn.apply(this, args);
          fn.memoize[hash] = result;
          return result;
        }
    };
};

var breakUp = cappedMemoize(function(str, hint){
  return new BreakString(str).break(hint);
}, 100);

shortDate = function(date){
  return moment(date).format(I18n.t("dates.medium.date_year"));
};

shortDateNoYear = function(date) {
  return moment(date).format(I18n.t("dates.tiny.date_month"));
};

tinyDateYear = function(date) {
  return moment(date).format(I18n.t("dates.tiny.date_year"));
};

// http://stackoverflow.com/questions/196972/convert-string-to-title-case-with-javascript
// TODO: locale support ?
toTitleCase = function toTitleCase(str) {
  return str.replace(/\w\S*/g, function(txt){
    return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
  });
};

longDate = function(dt) {
  if (!dt) return;
  return moment(dt).longDate();
};

// suppress year, if current year
longDateNoYear = function(dt) {
  if (!dt) return;

  if ((new Date()).getFullYear() !== dt.getFullYear()) {
    return moment(dt).format(I18n.t("dates.long_date_with_year"));
  } else {
    return moment(dt).format(I18n.t("dates.long_date_without_year"));
  }
};

updateRelativeAge = function(elems) {
  // jQuery .each
  elems.each(function(){
    var $this = $(this);
    $this.html(relativeAge(new Date($this.data('time')), {format: $this.data('format'), wrapInSpan: false}));
  });
};

autoUpdatingRelativeAge = function(date,options) {
  if (!date) return "";
  if (+date === +new Date(0)) return "";

  options = options || {};
  var format = options.format || "tiny";

  var append = "";

  if(format === 'medium') {
    append = " date";
    if(options.leaveAgo) {
      format = 'medium-with-ago';
    }
    options.wrapInSpan = false;
  }

  var relAge = relativeAge(date, options);

  if (format === 'tiny' && relativeAgeTinyShowsYear(relAge)) {
    append += " with-year";
  }

  if (options.title) {
    append += "' title='" + longDate(date);
  }

  return "" + relAge  + "";
};

relativeAgeTiny = function(date){
  var format = "tiny";
  var distance = Math.round((new Date() - date) / 1000);
  var distanceInMinutes = Math.round(distance / 60.0);

  var formatted;
  var t = function(key,opts){
    return I18n.t("dates." + format + "." + key, opts);
  };

  switch(true){

  case(distanceInMinutes < 1):
    formatted = t("less_than_x_minutes", {count: 1});
    break;
  case(distanceInMinutes >= 1 && distanceInMinutes <= 44):
    formatted = t("x_minutes", {count: distanceInMinutes});
    break;
  case(distanceInMinutes >= 45 && distanceInMinutes <= 89):
    formatted = t("about_x_hours", {count: 1});
    break;
  case(distanceInMinutes >= 90 && distanceInMinutes <= 1439):
    formatted = t("about_x_hours", {count: Math.round(distanceInMinutes / 60.0)});
    break;
  case(Discourse.SiteSettings.relative_date_duration === 0 && distanceInMinutes <= 525599):
    formatted = shortDateNoYear(date);
    break;
  case(distanceInMinutes >= 1440 && distanceInMinutes <= 2519):
    formatted = t("x_days", {count: 1});
    break;
  case(distanceInMinutes >= 2520 && distanceInMinutes <= ((Discourse.SiteSettings.relative_date_duration||14) * 1440)):
    formatted = t("x_days", {count: Math.round(distanceInMinutes / 1440.0)});
    break;
  default:
    if(date.getFullYear() === new Date().getFullYear()) {
      formatted = shortDateNoYear(date);
    } else {
      formatted = tinyDateYear(date);
    }
    break;
  }

  return formatted;
};

/*
 * Returns true if the given tiny date string includes the year.
 * Useful for checking if the string isn't so tiny.
 */
relativeAgeTinyShowsYear = function(relativeAgeString) {
  return relativeAgeString.match(/'[\d]{2}$/);
};

relativeAgeMediumSpan = function(distance, leaveAgo) {
  var formatted, distanceInMinutes;

  distanceInMinutes = Math.round(distance / 60.0);

  var t = function(key, opts){
    return I18n.t("dates.medium" + (leaveAgo?"_with_ago":"") + "." + key, opts);
  };

  switch(true){
  case(distanceInMinutes >= 1 && distanceInMinutes <= 56):
    formatted = t("x_minutes", {count: distanceInMinutes});
    break;
  case(distanceInMinutes >= 56 && distanceInMinutes <= 89):
    formatted = t("x_hours", {count: 1});
    break;
  case(distanceInMinutes >= 90 && distanceInMinutes <= 1379):
    formatted = t("x_hours", {count: Math.round(distanceInMinutes / 60.0)});
    break;
  case(distanceInMinutes >= 1380 && distanceInMinutes <= 2159):
    formatted = t("x_days", {count: 1});
    break;
  case(distanceInMinutes >= 2160):
    formatted = t("x_days", {count: Math.round((distanceInMinutes - 720.0) / 1440.0)});
    break;
  }
  return formatted || '&mdash';
};

relativeAgeMedium = function(date, options){
  var displayDate, fiveDaysAgo, oneMinuteAgo, fullReadable, leaveAgo;
  var wrapInSpan = options.wrapInSpan !== false;

  leaveAgo = options.leaveAgo;
  var distance = Math.round((new Date() - date) / 1000);

  if (!date) {
    return "—";
  }

  fullReadable = longDate(date);
  displayDate = "";
  fiveDaysAgo = 432000;
  oneMinuteAgo = 60;

  if (distance < oneMinuteAgo) {
    displayDate = I18n.t("now");
  } else if (distance > fiveDaysAgo) {
    if ((new Date()).getFullYear() !== date.getFullYear()) {
      displayDate = shortDate(date);
    } else {
      displayDate = shortDateNoYear(date);
    }
  } else {
    displayDate = relativeAgeMediumSpan(distance, leaveAgo);
  }
  if(wrapInSpan) {
    return "" + displayDate + "";
  } else {
    return displayDate;
  }
};

// mostly lifted from rails with a few amendments
relativeAge = function(date, options) {
  options = options || {};
  var format = options.format || "tiny";

  if(format === "tiny") {
    return relativeAgeTiny(date, options);
  } else if (format === "medium") {
    return relativeAgeMedium(date, options);
  } else if (format === 'medium-with-ago') {
    return relativeAgeMedium(date, _.extend(options, {format: 'medium', leaveAgo: true}));
  }

  return "UNKNOWN FORMAT";
};

var number = function(val) {
  val = parseInt(val, 10);
  if (isNaN(val)) val = 0;

  if (val > 999) {
    return (val / 1000).toFixed(1) + "K";
  }
  return val.toString();
};

Discourse.Formatter = {
  longDate: longDate,
  longDateNoYear: longDateNoYear,
  relativeAge: relativeAge,
  autoUpdatingRelativeAge: autoUpdatingRelativeAge,
  updateRelativeAge: updateRelativeAge,
  toTitleCase: toTitleCase,
  shortDate: shortDate,
  breakUp: breakUp,
  cappedMemoize: cappedMemoize,
  number: number
};
<|endoftext|>


<commit_before>WebsiteOne.renderHangoutButton = function() {
  var placeholder = 'liveHOA-placeholder',
      hoa_placeholder = $('#' + placeholder);

  if ( typeof(gapi) != 'undefined' &&
       hoa_placeholder.length > 0 &&
       $('#' + placeholder + ' iframe').length == 0 ) {

    var topic = hoa_placeholder.data('topic'),
        app_id = hoa_placeholder.data('app-id'),
        callback_url = hoa_placeholder.data('callback-url');

    gapi.hangout.render(placeholder, {
        render: 'createhangout',
        topic: topic,
        hangout_type: 'onair',
        initial_apps: [{
          app_id: app_id,
          start_data: callback_url,
          app_type : 'ROOM_APP'
        }],
        widget_size: 200
    });
  };
};

WebsiteOne.loadHangoutsApi = function() {
  if (typeof(gapi) === 'undefined') {
    $.ajax({
      url: 'https://apis.google.com/js/platform.js',
      dataType: "script",
      cache: true
    }).done(WebsiteOne.renderHangoutButton);
  }
};

WebsiteOne.define('Hangouts', function() {
  return {
    init: function() {
      WebsiteOne.renderHangoutButton();
    }
  }
});

WebsiteOne.loadHangoutsApi();
<commit_msg>minor refactoring<commit_after>WebsiteOne.renderHangoutButton = function() {
  var placeholder = 'liveHOA-placeholder',
      hoa_placeholder = $('#' + placeholder);

  if ( typeof(gapi) != 'undefined' &&
       hoa_placeholder.length > 0 &&
       $('#' + placeholder + ' iframe').length == 0 ) {

    var topic = hoa_placeholder.data('topic'),
        app_id = hoa_placeholder.data('app-id'),
        callback_url = hoa_placeholder.data('callback-url');

    gapi.hangout.render(placeholder, {
        render: 'createhangout',
        topic: topic,
        hangout_type: 'onair',
        initial_apps: [{
          app_id: app_id,
          start_data: callback_url,
          app_type : 'ROOM_APP'
        }],
        widget_size: 200
    });
  };
};

WebsiteOne.loadHangoutsApi = function() {
  if (typeof(gapi) === 'undefined') {
    $.ajax({
      url: 'https://apis.google.com/js/platform.js',
      dataType: "script",
      cache: true
    }).done(WebsiteOne.renderHangoutButton);
  }
};

WebsiteOne.define('Hangouts', function() {
  return {
    init: function() {
      WebsiteOne.renderHangoutButton();
    }
  }
});

WebsiteOne.loadHangoutsApi();

<|endoftext|>


<commit_before>(function () {
  'use strict'
  var root = this
  var $ = root.jQuery

  if (typeof GOVUK === 'undefined') { root.GOVUK = {} }

  var SelectionButtons = function (elmsOrSelector, opts) {
    this.selectedClass = 'selected'
    this.focusedClass = 'focused'
    if (opts !== undefined) {
      $.each(opts, function (optionName, optionObj) {
        this[optionName] = optionObj
      }.bind(this))
    }
    if (typeof elmsOrSelector === 'string') {
      this.$elms = $(elmsOrSelector)
      this.selector = elmsOrSelector
      this.setInitialState($(this.selector))
    } else if (elmsOrSelector !== undefined) {
      this.$elms = elmsOrSelector
      this.setInitialState(this.$elms)
    }
    this.addEvents()
  }
  SelectionButtons.prototype.addEvents = function () {
    if (typeof this.$elms !== 'undefined') {
      this.addElementLevelEvents()
    } else {
      this.addDocumentLevelEvents()
    }
  }
  SelectionButtons.prototype.setInitialState = function ($elms) {
    $elms.each(function (idx, elm) {
      var $elm = $(elm)

      if ($elm.is(':checked')) {
        this.markSelected($elm)
      }
    }.bind(this))
  }
  SelectionButtons.prototype.markFocused = function ($elm, state) {
    if (state === 'focused') {
      $elm.parent('label').addClass(this.focusedClass)
    } else {
      $elm.parent('label').removeClass(this.focusedClass)
    }
  }
  SelectionButtons.prototype.markSelected = function ($elm) {
    var radioName

    if ($elm.attr('type') === 'radio') {
      radioName = $elm.attr('name')
      $($elm[0].form).find('input[name="' + radioName + '"]')
        .parent('label')
        .removeClass(this.selectedClass)
      $elm.parent('label').addClass(this.selectedClass)
    } else { // checkbox
      if ($elm.is(':checked')) {
        $elm.parent('label').addClass(this.selectedClass)
      } else {
        $elm.parent('label').removeClass(this.selectedClass)
      }
    }
  }
  SelectionButtons.prototype.addElementLevelEvents = function () {
    this.clickHandler = this.getClickHandler()
    this.focusHandler = this.getFocusHandler({ 'level': 'element' })

    this.$elms
      .on('click', this.clickHandler)
      .on('focus blur', this.focusHandler)
  }
  SelectionButtons.prototype.addDocumentLevelEvents = function () {
    this.clickHandler = this.getClickHandler()
    this.focusHandler = this.getFocusHandler({ 'level': 'document' })

    $(document)
      .on('click', this.selector, this.clickHandler)
      .on('focus blur', this.selector, this.focusHandler)
  }
  SelectionButtons.prototype.getClickHandler = function () {
    return function (e) {
      this.markSelected($(e.target))
    }.bind(this)
  }
  SelectionButtons.prototype.getFocusHandler = function (opts) {
    var focusEvent = (opts.level === 'document') ? 'focusin' : 'focus'

    return function (e) {
      var state = (e.type === focusEvent) ? 'focused' : 'blurred'

      this.markFocused($(e.target), state)
    }.bind(this)
  }
  SelectionButtons.prototype.destroy = function () {
    if (typeof this.selector !== 'undefined') {
      $(document)
        .off('click', this.selector, this.clickHandler)
        .off('focus blur', this.selector, this.focusHandler)
    } else {
      this.$elms
        .off('click', this.clickHandler)
        .off('focus blur', this.focusHandler)
    }
  }

  root.GOVUK.SelectionButtons = SelectionButtons
}).call(this)
<commit_msg>Remove duplicate selection-buttons.js<commit_after><|endoftext|>


<commit_before>/**
 * The layers filter module.
 *
 * @return singleton instance of layers fitler class (extends Backbone.View).
 */
define([
  'backbone',
  'underscore',
  'amplify',
  'chosen',
  'map/presenters/LayersNavPresenter',
  'handlebars',
  'text!map/templates/layersNav.handlebars',
  'text!map/templates/layersNavByCountry.handlebars'
], function(Backbone, _, amplify, chosen, Presenter, Handlebars, tpl, tplCountry) {

  'use strict';

  var LayersNavView = Backbone.View.extend({

    el: '.layers-menu',

    template: Handlebars.compile(tpl),
    templateCountry: Handlebars.compile(tplCountry),

    events: {
      'click .layer': '_toggleLayer',
      'change #country-select' : 'changeIso'
    },

    initialize: function() {
      _.bindAll(this, '_toggleSelected');
      this.presenter = new Presenter(this);
      this.render();
    },

    render: function() {
      this.$el.append(this.template());
      //Experiment
      this.presenter.initExperiment('source');

      //Init
      this.$layersCountry = $('#layers-country-nav');
      this.$countrySublayer = $('.country-sublayer-box')
      this.getCountries();
    },

    /**
     * Used by LayersNavPresenter to toggle the class
     * name selected.
     *
     * @param  {object} layerSpec
     */
    _toggleSelected: function(layers) {
      // Toggle sublayers
      _.each(this.$el.find('.layer'), function(li) {
        var $li = $(li);
        var $toggle = $li.find('.onoffradio, .onoffswitch');
        var $toggleIcon = $toggle.find('span');
        var $layerTitle = $li.find('.layer-title');
        var layer = layers[$li.data('layer')];

        if (layer) {
          var isBaselayer = (layer.category_slug === 'forest_clearing');

          $li.addClass('selected');
          $toggle.addClass('checked');
          $layerTitle.css('color', layer.title_color);

          if (!isBaselayer) {
            $toggle.css('background', layer.title_color);
          } else {
            $toggle.css('border-color', layer.title_color);
            $toggleIcon.css('background-color', layer.title_color);
          }
          ga('send', 'event', 'Map', 'Toogle', 'Layer: ' + layer.slug);
        } else {
          $li.removeClass('selected');
          $toggle.removeClass('checked').css('background', '').css('border-color', '');
          $toggleIcon.css('background-color', '');
          $layerTitle.css('color', '');
        }
      });
    },

    /**
     * Handles a toggle layer change UI event by dispatching
     * to LayersNavPresenter.
     *
     * @param  {event} event Click event
     */
    _toggleLayer: function(event) {
      event && event.preventDefault();
      // this prevents layer change when you click in source link
      if (!$(event.target).hasClass('source')) {
        var layerSlug = $(event.currentTarget).data('layer');

        if ($(event.currentTarget).hasClass('ifl') || $(event.currentTarget).hasClass('c_f_peru')) {
            var fil_type = 'ifl_2013_deg';
          if ($(event.currentTarget).hasClass('c_f_peru')) {
            fil_type = 'concesiones_forestalesNS';
          }
          event && event.stopPropagation();
          var $elem = $(event.currentTarget);
            if (event.target.nodeName === 'LABEL') {
              $elem.find('input').click();
              return false;
            }
          if ($elem.hasClass('selected')) {$elem.find('input').prop('checked',false);}
          else {$elem.find('[data-layer="' + fil_type + '"] input').prop('checked', true);}
          if ($elem.prop('tagName') !== 'LI'){
            for (var i=0;i < $elem.siblings().length; i++) {
              if ($($elem.siblings()[i]).hasClass('selected')) {
                this.presenter.toggleLayer($($elem.siblings()[i]).data('layer'));
              }
              $elem.parents('li').data('layer' , $elem.data('layer')).addClass('selected');
            }
          }
        }
        this.presenter.toggleLayer(layerSlug);
        ga('send', 'event', 'Map', 'Toogle', 'Layer: ' + layerSlug);
      }
    },

    _getIsoLayers: function(layers) {
      this.layersIso = layers;
    },

    /**
     * Ajax for getting countries.
     */
    getCountries: function(){
      if (!amplify.store('countries')) {
        var sql = ['SELECT c.iso, c.name FROM gfw2_countries c WHERE c.enabled = true'];
        $.ajax({
          url: 'https://wri-01.cartodb.com/api/v2/sql?q='+sql,
          dataType: 'json',
          success: _.bind(function(data){
            amplify.store('countries', data.rows);
            this.printCountries();
          }, this ),
          error: function(error){
            console.log(error);
          }
        });
      }else{
        this.printCountries()
      }
    },

    /**
     * Print countries.
     */
    printCountries: function(){
      //Country select
      this.$countrySelect = $('#country-select');
      this.countries = amplify.store('countries');

      //Loop for print options
      var options = "<option></option>";
      _.each(_.sortBy(this.countries, function(country){ return country.name }), _.bind(function(country, i){
        options += '<option value="'+ country.iso +'">'+ country.name + '</option>';
      }, this ));
      this.$countrySelect.append(options);
      this.$countrySelect.chosen({
        width: '100%',
        allow_single_deselect: true,
        inherit_select_classes: true,
        no_results_text: "Oops, nothing found!"
      });

    },

    // Select change iso
    changeIso: function(){
      this.iso = this.$countrySelect.val();
      _.each(this.$layersCountry.find('.layer'), _.bind(function(el){
        ($(el).hasClass('selected')) ? $(el).trigger('click') : null;
      }, this ));
      this.setIsoLayers();
      if (this.iso) {
        this.presenter._analizeIso(this.iso);
      }
    },

    // For autoselect country and region when youn reload page
    setSelect: function(iso){
      this.$countrySelect.val(iso.country).trigger("liszt:updated");
      this.iso = this.$countrySelect.val();
      _.each(this.$layersCountry.find('.layer'), _.bind(function(el){
        ($(el).hasClass('selected')) ? $(el).trigger('click') : null;
      }, this ));
      this.setIsoLayers();
    },

    /**
     * Render Iso Layers.
     */
    setIsoLayers: function(e){
      var layersToRender = [];
      _.each(this.layersIso, _.bind(function(layer){
        if (layer.iso === this.iso) {
          layersToRender.push(layer);
        }
      }, this ));
      if (layersToRender.length > 0) {
        this.$countrySublayer.addClass('active');
      }else{
        this.$countrySublayer.removeClass('active');
      }
      this.renderIsoLayers(layersToRender);
    },

    renderIsoLayers: function(layers){
      this.$layersCountry.html(this.templateCountry({layers: layers }));
    }

  });

  return LayersNavView;

});
<commit_msg>Map: country Select fix<commit_after>/**
 * The layers filter module.
 *
 * @return singleton instance of layers fitler class (extends Backbone.View).
 */
define([
  'backbone',
  'underscore',
  'amplify',
  'chosen',
  'map/presenters/LayersNavPresenter',
  'handlebars',
  'text!map/templates/layersNav.handlebars',
  'text!map/templates/layersNavByCountry.handlebars'
], function(Backbone, _, amplify, chosen, Presenter, Handlebars, tpl, tplCountry) {

  'use strict';

  var LayersNavView = Backbone.View.extend({

    el: '.layers-menu',

    template: Handlebars.compile(tpl),
    templateCountry: Handlebars.compile(tplCountry),

    events: {
      'click .layer': '_toggleLayer',
      'change #country-select' : 'changeIso'
    },

    initialize: function() {
      _.bindAll(this, '_toggleSelected');
      this.presenter = new Presenter(this);
      this.render();
    },

    render: function() {
      this.$el.append(this.template());
      //Experiment
      this.presenter.initExperiment('source');

      //Init
      this.$countrySelect = $('#country-select');
      this.$layersCountry = $('#layers-country-nav');
      this.$countrySublayer = $('.country-sublayer-box')
      this.getCountries();
    },

    /**
     * Used by LayersNavPresenter to toggle the class
     * name selected.
     *
     * @param  {object} layerSpec
     */
    _toggleSelected: function(layers) {
      // Toggle sublayers
      _.each(this.$el.find('.layer'), function(li) {
        var $li = $(li);
        var $toggle = $li.find('.onoffradio, .onoffswitch');
        var $toggleIcon = $toggle.find('span');
        var $layerTitle = $li.find('.layer-title');
        var layer = layers[$li.data('layer')];

        if (layer) {
          var isBaselayer = (layer.category_slug === 'forest_clearing');

          $li.addClass('selected');
          $toggle.addClass('checked');
          $layerTitle.css('color', layer.title_color);

          if (!isBaselayer) {
            $toggle.css('background', layer.title_color);
          } else {
            $toggle.css('border-color', layer.title_color);
            $toggleIcon.css('background-color', layer.title_color);
          }
          ga('send', 'event', 'Map', 'Toogle', 'Layer: ' + layer.slug);
        } else {
          $li.removeClass('selected');
          $toggle.removeClass('checked').css('background', '').css('border-color', '');
          $toggleIcon.css('background-color', '');
          $layerTitle.css('color', '');
        }
      });
    },

    /**
     * Handles a toggle layer change UI event by dispatching
     * to LayersNavPresenter.
     *
     * @param  {event} event Click event
     */
    _toggleLayer: function(event) {
      event && event.preventDefault();
      // this prevents layer change when you click in source link
      if (!$(event.target).hasClass('source')) {
        var layerSlug = $(event.currentTarget).data('layer');

        if ($(event.currentTarget).hasClass('ifl') || $(event.currentTarget).hasClass('c_f_peru')) {
            var fil_type = 'ifl_2013_deg';
          if ($(event.currentTarget).hasClass('c_f_peru')) {
            fil_type = 'concesiones_forestalesNS';
          }
          event && event.stopPropagation();
          var $elem = $(event.currentTarget);
            if (event.target.nodeName === 'LABEL') {
              $elem.find('input').click();
              return false;
            }
          if ($elem.hasClass('selected')) {$elem.find('input').prop('checked',false);}
          else {$elem.find('[data-layer="' + fil_type + '"] input').prop('checked', true);}
          if ($elem.prop('tagName') !== 'LI'){
            for (var i=0;i < $elem.siblings().length; i++) {
              if ($($elem.siblings()[i]).hasClass('selected')) {
                this.presenter.toggleLayer($($elem.siblings()[i]).data('layer'));
              }
              $elem.parents('li').data('layer' , $elem.data('layer')).addClass('selected');
            }
          }
        }
        this.presenter.toggleLayer(layerSlug);
        ga('send', 'event', 'Map', 'Toogle', 'Layer: ' + layerSlug);
      }
    },

    _getIsoLayers: function(layers) {
      this.layersIso = layers;
    },

    /**
     * Ajax for getting countries.
     */
    getCountries: function(){
      if (!amplify.store('countries')) {
        var sql = ['SELECT c.iso, c.name FROM gfw2_countries c WHERE c.enabled = true'];
        $.ajax({
          url: 'https://wri-01.cartodb.com/api/v2/sql?q='+sql,
          dataType: 'json',
          success: _.bind(function(data){
            amplify.store('countries', data.rows);
            this.printCountries();
          }, this ),
          error: function(error){
            console.log(error);
          }
        });
      }else{
        this.printCountries()
      }
    },

    /**
     * Print countries.
     */
    printCountries: function(){
      //Country select
      this.countries = amplify.store('countries');

      //Loop for print options
      var options = "<option></option>";
      _.each(_.sortBy(this.countries, function(country){ return country.name }), _.bind(function(country, i){
        options += '<option value="'+ country.iso +'">'+ country.name + '</option>';
      }, this ));
      this.$countrySelect.append(options);
      this.$countrySelect.chosen({
        width: '100%',
        allow_single_deselect: true,
        inherit_select_classes: true,
        no_results_text: "Oops, nothing found!"
      });

    },

    // Select change iso
    changeIso: function(){
      this.iso = this.$countrySelect.val();
      _.each(this.$layersCountry.find('.layer'), _.bind(function(el){
        ($(el).hasClass('selected')) ? $(el).trigger('click') : null;
      }, this ));
      this.setIsoLayers();
      if (this.iso) {
        this.presenter._analizeIso(this.iso);
      }
    },

    // For autoselect country and region when youn reload page
    setSelect: function(iso){
      this.$countrySelect.val(iso.country).trigger("liszt:updated");
      this.iso = this.$countrySelect.val();
      _.each(this.$layersCountry.find('.layer'), _.bind(function(el){
        ($(el).hasClass('selected')) ? $(el).trigger('click') : null;
      }, this ));
      this.setIsoLayers();
    },

    /**
     * Render Iso Layers.
     */
    setIsoLayers: function(e){
      var layersToRender = [];
      _.each(this.layersIso, _.bind(function(layer){
        if (layer.iso === this.iso) {
          layersToRender.push(layer);
        }
      }, this ));
      if (layersToRender.length > 0) {
        this.$countrySublayer.addClass('active');
      }else{
        this.$countrySublayer.removeClass('active');
      }
      this.renderIsoLayers(layersToRender);
    },

    renderIsoLayers: function(layers){
      this.$layersCountry.html(this.templateCountry({layers: layers }));
    }

  });

  return LayersNavView;

});
<|endoftext|>


<commit_before>/**
 * The layers filter module.
 *
 * @return singleton instance of layers fitler class (extends Backbone.View).
 */
define([
  'backbone',
  'underscore',
  'amplify',
  'chosen',
  'map/presenters/LayersNavPresenter',
  'handlebars',
  'text!map/templates/layersNav.handlebars',
  'text!map/templates/layersNavByCountry.handlebars'
], function(Backbone, _, amplify, chosen, Presenter, Handlebars, tpl, tplCountry) {

  'use strict';

  var LayersNavView = Backbone.View.extend({

    el: '.layers-menu',

    template: Handlebars.compile(tpl),
    templateCountry: Handlebars.compile(tplCountry),

    events: {
      'click .layer': '_toggleLayer',
      'click #country-layers' : '_showNotification'
    },

    initialize: function() {
      _.bindAll(this, '_toggleSelected');
      this.presenter = new Presenter(this);
      this.render();
    },

    render: function() {
      this.$el.append(this.template());
      //Experiment
      this.presenter.initExperiment('source');

      //Init
      this.$categoriesList = $('.categories-list');
      this.$layersCountry = $('#layers-country-nav');
      this.$countryLayers = $('#country-layers');
    },

    /**
     * Used by LayersNavPresenter to toggle the class
     * name selected.
     *
     * @param  {object} layerSpec
     */
    _toggleSelected: function(layers) {

      this.layers = layers;

      // Toggle sublayers
      _.each(this.$el.find('.layer'), function(li) {
        var $li = $(li);
        var $toggle = $li.find('.onoffradio, .onoffswitch');
        var $toggleIcon = $toggle.find('span');
        var $layerTitle = $li.find('.layer-title');
        var layer = layers[$li.data('layer')];

        if (layer) {
          var isBaselayer = (layer.category_slug === 'forest_clearing');

          $li.addClass('selected');
          $toggle.addClass('checked');
          $layerTitle.css('color', layer.title_color);

          if (!isBaselayer) {
            $toggle.css('background', layer.title_color);
          } else {
            $toggle.css('border-color', layer.title_color);
            $toggleIcon.css('background-color', layer.title_color);
          }
          ga('send', 'event', 'Map', 'Toggle', 'Layer: ' + layer.slug);
        } else {
          $li.removeClass('selected');
          $toggle.removeClass('checked').css('background', '').css('border-color', '');
          $toggleIcon.css('background-color', '');
          $layerTitle.css('color', '');
        }
      });
    },

    /**
     * Handles a toggle layer change UI event by dispatching
     * to LayersNavPresenter.
     *
     * @param  {event} event Click event
     */
    _toggleLayer: function(event) {
      event && event.preventDefault();
      // this prevents layer change when you click in source link
      if (!$(event.target).hasClass('source') && !$(event.target).parent().hasClass('source')) {
        var layerSlug = $(event.currentTarget).data('layer');

        if ($(event.currentTarget).hasClass('ifl')) {
          event && event.stopPropagation();
          var $elem = $(event.currentTarget);
            if (event.target.nodeName === 'LABEL') {
              $elem.find('input').click();
              return false;
            }
          if ($elem.hasClass('selected')) {$elem.find('input').prop('checked',false);}
          else {$elem.find('[data-layer="ifl_2013_deg"] input').prop('checked', true);}
          if ($elem.prop('tagName') !== 'LI'){
            for (var i=0;i < $elem.siblings().length; i++) {
              if ($($elem.siblings()[i]).hasClass('selected')) {
                this.presenter.toggleLayer($($elem.siblings()[i]).data('layer'));
              }
              $elem.parents('li').data('layer' , $elem.data('layer')).addClass('selected');
            }
          }
        }
        this.presenter.toggleLayer(layerSlug);
        ga('send', 'event', 'Map', 'Toggle', 'Layer: ' + layerSlug);
      }
    },

    setIso: function(iso){
      this.iso = iso.country;
      this.region = iso.region;
      this.setIsoLayers();
    },

    updateIso: function(iso){
      // This is for preventing blur on layers nav
      this.$categoriesList.width('auto');
      (iso.country !== this.iso) ? this.resetIsoLayers() : null;
      this.iso = iso.country;
      this.region = iso.region;
      this.setIsoLayers();
    },

    /**
     * Render Iso Layers.
     */
    _getIsoLayers: function(layers) {
      this.layersIso = layers;
    },

    resetIsoLayers: function(){
      _.each(this.$countryLayers.find('.layer'),function(li){
        if ($(li).hasClass('selected')) {
          $(li).trigger('click');
        }
      })
    },

    setIsoLayers: function(e){
      var layersToRender = [];
      _.each(this.layersIso, _.bind(function(layer){
        if (layer.iso === this.iso) {
          layersToRender.push(layer);
        }
      }, this ));
      (layersToRender.length > 0) ? this.$countryLayers.addClass('active').removeClass('disabled') : this.$countryLayers.removeClass('active').addClass('disabled');
      this.renderIsoLayers(layersToRender);
    },

    renderIsoLayers: function(layers){
      console.log(layers)
      var country = _.find(amplify.store('countries'), _.bind(function(country){
        return country.iso === this.iso;
      }, this ));
      var name = (country) ? country.name : 'Country';
      (country) ? this.$countryLayers.addClass('iso-detected') : this.$countryLayers.removeClass('iso-detected');

      this.$countryLayers.html(this.templateCountry({ country: name ,  layers: layers }));

      this.fixLegibility();

      this.presenter.initExperiment('source');
      this._toggleSelected(this.layers);
    },

    fixLegibility: function(){
      var w = this.$categoriesList.width();
      if (w%2 != 0) {
        // This is for preventing blur on layers nav
        this.$categoriesList.width(w+1);
      }

    },

    _showNotification: function(e){
      if ($(e.currentTarget).hasClass('disabled')) {
        ($(e.currentTarget).hasClass('iso-detected')) ? this.presenter.notificate('country-not-layers') : this.presenter.notificate('country-choose');
      }
    }

  });

  return LayersNavView;

});
<commit_msg>cleaning<commit_after>/**
 * The layers filter module.
 *
 * @return singleton instance of layers fitler class (extends Backbone.View).
 */
define([
  'backbone',
  'underscore',
  'amplify',
  'chosen',
  'map/presenters/LayersNavPresenter',
  'handlebars',
  'text!map/templates/layersNav.handlebars',
  'text!map/templates/layersNavByCountry.handlebars'
], function(Backbone, _, amplify, chosen, Presenter, Handlebars, tpl, tplCountry) {

  'use strict';

  var LayersNavView = Backbone.View.extend({

    el: '.layers-menu',

    template: Handlebars.compile(tpl),
    templateCountry: Handlebars.compile(tplCountry),

    events: {
      'click .layer': '_toggleLayer',
      'click #country-layers' : '_showNotification'
    },

    initialize: function() {
      _.bindAll(this, '_toggleSelected');
      this.presenter = new Presenter(this);
      this.render();
    },

    render: function() {
      this.$el.append(this.template());
      //Experiment
      this.presenter.initExperiment('source');

      //Init
      this.$categoriesList = $('.categories-list');
      this.$layersCountry = $('#layers-country-nav');
      this.$countryLayers = $('#country-layers');
    },

    /**
     * Used by LayersNavPresenter to toggle the class
     * name selected.
     *
     * @param  {object} layerSpec
     */
    _toggleSelected: function(layers) {

      this.layers = layers;

      // Toggle sublayers
      _.each(this.$el.find('.layer'), function(li) {
        var $li = $(li);
        var $toggle = $li.find('.onoffradio, .onoffswitch');
        var $toggleIcon = $toggle.find('span');
        var $layerTitle = $li.find('.layer-title');
        var layer = layers[$li.data('layer')];

        if (layer) {
          var isBaselayer = (layer.category_slug === 'forest_clearing');

          $li.addClass('selected');
          $toggle.addClass('checked');
          $layerTitle.css('color', layer.title_color);

          if (!isBaselayer) {
            $toggle.css('background', layer.title_color);
          } else {
            $toggle.css('border-color', layer.title_color);
            $toggleIcon.css('background-color', layer.title_color);
          }
          ga('send', 'event', 'Map', 'Toggle', 'Layer: ' + layer.slug);
        } else {
          $li.removeClass('selected');
          $toggle.removeClass('checked').css('background', '').css('border-color', '');
          $toggleIcon.css('background-color', '');
          $layerTitle.css('color', '');
        }
      });
    },

    /**
     * Handles a toggle layer change UI event by dispatching
     * to LayersNavPresenter.
     *
     * @param  {event} event Click event
     */
    _toggleLayer: function(event) {
      event && event.preventDefault();
      // this prevents layer change when you click in source link
      if (!$(event.target).hasClass('source') && !$(event.target).parent().hasClass('source')) {
        var layerSlug = $(event.currentTarget).data('layer');

        if ($(event.currentTarget).hasClass('ifl')) {
          event && event.stopPropagation();
          var $elem = $(event.currentTarget);
            if (event.target.nodeName === 'LABEL') {
              $elem.find('input').click();
              return false;
            }
          if ($elem.hasClass('selected')) {$elem.find('input').prop('checked',false);}
          else {$elem.find('[data-layer="ifl_2013_deg"] input').prop('checked', true);}
          if ($elem.prop('tagName') !== 'LI'){
            for (var i=0;i < $elem.siblings().length; i++) {
              if ($($elem.siblings()[i]).hasClass('selected')) {
                this.presenter.toggleLayer($($elem.siblings()[i]).data('layer'));
              }
              $elem.parents('li').data('layer' , $elem.data('layer')).addClass('selected');
            }
          }
        }
        this.presenter.toggleLayer(layerSlug);
        ga('send', 'event', 'Map', 'Toggle', 'Layer: ' + layerSlug);
      }
    },

    setIso: function(iso){
      this.iso = iso.country;
      this.region = iso.region;
      this.setIsoLayers();
    },

    updateIso: function(iso){
      // This is for preventing blur on layers nav
      this.$categoriesList.width('auto');
      (iso.country !== this.iso) ? this.resetIsoLayers() : null;
      this.iso = iso.country;
      this.region = iso.region;
      this.setIsoLayers();
    },

    /**
     * Render Iso Layers.
     */
    _getIsoLayers: function(layers) {
      this.layersIso = layers;
    },

    resetIsoLayers: function(){
      _.each(this.$countryLayers.find('.layer'),function(li){
        if ($(li).hasClass('selected')) {
          $(li).trigger('click');
        }
      })
    },

    setIsoLayers: function(e){
      var layersToRender = [];
      _.each(this.layersIso, _.bind(function(layer){
        if (layer.iso === this.iso) {
          layersToRender.push(layer);
        }
      }, this ));
      (layersToRender.length > 0) ? this.$countryLayers.addClass('active').removeClass('disabled') : this.$countryLayers.removeClass('active').addClass('disabled');
      this.renderIsoLayers(layersToRender);
    },

    renderIsoLayers: function(layers){
      var country = _.find(amplify.store('countries'), _.bind(function(country){
        return country.iso === this.iso;
      }, this ));
      var name = (country) ? country.name : 'Country';
      (country) ? this.$countryLayers.addClass('iso-detected') : this.$countryLayers.removeClass('iso-detected');

      this.$countryLayers.html(this.templateCountry({ country: name ,  layers: layers }));

      this.fixLegibility();

      this.presenter.initExperiment('source');
      this._toggleSelected(this.layers);
    },

    fixLegibility: function(){
      var w = this.$categoriesList.width();
      if (w%2 != 0) {
        // This is for preventing blur on layers nav
        this.$categoriesList.width(w+1);
      }

    },

    _showNotification: function(e){
      if ($(e.currentTarget).hasClass('disabled')) {
        ($(e.currentTarget).hasClass('iso-detected')) ? this.presenter.notificate('country-not-layers') : this.presenter.notificate('country-choose');
      }
    }

  });

  return LayersNavView;

});
<|endoftext|>