<commit_before>/*eslint no-unused-vars: [2, "local"] */
/*global ParameterChanged:true, HandleMIDI:true,
  SetParameter, UpdatePluginParameters, MIDI, Note */

const MODELS = {}

// This object defines Drum Machine Designer's sound groups and
// each sound's pitch offset from C1. E.g. KICK 1 is C1+0 while
// KICK 2 is F2+17. It's used for a few things internally as
// well as specifying the groups' dropdown order in the UI.
const DRUMMER = {
  'KICK': [0, 17],
  'SNARE': [2, 4, 16],
  'RIM': [1],
  'CLAP': [3, 22],
  'PERC': [5, 7, 9, 11, 12],
  'CH': [6],
  'PH': [8],
  'OH': [10],
  'CRASH': [13],
  'RIDE': [15],
  'SHAKER': [18, 14],
  'FX': [19, 21, 23, 20]
}

const PITCH_INPUT_OFFSET = -36 // C1 will become array index 0
const PITCH_OUTPUT_OFFSET = 60 // C3

// begin UI with first two dropdowns
var PluginParameters = [{
  name: 'Model',
  type: 'menu',
  valueStrings: Object.keys(MODELS),
  defaultValue: 0
}, {
  name: 'Randomize',
  type: 'menu',
  defaultValue: 1,
  valueStrings: ['Reset default', '-', 'KICK', 'SNARE', 'CLAP', 'PERC', 'FX']
}]

var App = (function () {
  var model, modelName
  var Sounds = []
  var _initComplete
  var _skipUpdates

  // one-time init
  for (var group in DRUMMER) {
    DRUMMER[group].forEach(function (pitchOffset, alt) {
      // sound map dropdown placeholders prevent Logic
      // from complaining about param changes
      PluginParameters.push({
        name: group + (DRUMMER[group].length > 1 ? ' ' + (alt + 1) : ''),
        type: 'menu',
        defaultValue: 0,
        valueStrings: ['-'].concat(Array(16).fill(''))
      })

      // array of objects 0-indexed relative to C1's MIDI pitch
      Sounds[pitchOffset] = {
        group,
        alt,
        param: PluginParameters.length - 1, // current PluginParameter index
        pad: false // not mapped yet
      }
    })
  }

  /**
   * map a sound's pad using the specified method
   * @param  {[type]} sound  [description]
   * @param  {[type]} method [description]
   * @return {[type]}        [description]
   */
  function mapSound (sound, method) {
    if (Array.isArray(sound)) {
      sound.forEach(function (s) {
        mapSound(s, method)
      })

      return
    }

    var altOut = (function (altIn, group, method) {
      // if there's not much choice...
      if (model[group].length === 0) return false
      if (model[group].length === 1) return 0

      if (method === 'random') {
        return Math.floor(Math.random() * model[group].length)
      }

      // default (linear transform)
      return Math.floor(
        altIn / (DRUMMER[group].length - 1) * (model[group].length - 1)
      )
    })(sound.alt, sound.group, method)

    sound.pad = (altOut === false) ? false : model[sound.group][altOut]
    SetParameter(sound.param, (sound.pad === false) ? 0 : sound.pad + 1)
  }

  function setModel (key, setDefaults) {
    var opts = ['-']

    // avoid doing anything if we can
    if (key === modelName) return
    modelName = key

    // group model sounds like in DRUMMER
    model = {}
    for (var group in DRUMMER) {
      model[group] = []
    }

    // parse model definition
    MODELS[modelName].forEach(function (arr, pad) {
      // allow for arrr-ayless shorthand, matey
      if (!Array.isArray(arr)) arr = [arr, arr]

      // sound assignments
      arr[1].toUpperCase().split('|').forEach(function (txt) {
        var [group, num] = txt.split(' ')
        if (!group) return

        // set pad's index/position (0-15)
        if (num && typeof model[group][num - 1] === 'undefined') {
          model[group][num - 1] = pad
        } else {
          // add to next available alt slot
          model[group].push(pad)
        }
      })

      // add pad to dropdown options
      opts.push(arr[0] + '   [' + MIDI.noteName(pad + PITCH_OUTPUT_OFFSET) + ']')
    })

      // update pad selection/dropdowns for this model
    for (var sound in Sounds) {
      PluginParameters[Sounds[sound].param].valueStrings = opts
    }

    // set default mapping?
    if (setDefaults) mapSound(Sounds, 'default')

    // need Logic to update dropdown options but don't
    // care about the ParameterChanged() call for each
    _skipUpdates = true
    UpdatePluginParameters()
  }

  function ParameterChanged (param, value) {
    if (_initComplete && _skipUpdates) {
      if (param === PluginParameters.length - 1) {
        _skipUpdates = false // update sweep is complete
      }
      return
    }

    // when Logic has finished restoring params for the first time
    if (!_initComplete && param === PluginParameters.length - 1) {
      _initComplete = true
    }

    if (param === 0) {
      // change model and set defaults if user initiated change
      setModel(PluginParameters[param].valueStrings[value], _initComplete)
    } else if (param === 1) {
      // "Edit" dropdown
      var group = PluginParameters[param].valueStrings[value]

      if (value === 0) {
        mapSound(Sounds, 'default') // reset defaults
      } else if (value > 1) {
        // randomize group
        mapSound(Sounds.filter(function (sound) {
          return sound.group === group
        }), 'random')
      }

      // set dropdown back to '-'
      SetParameter(param, 1)
    } else {
      // find the sound with this param index
      Sounds.find(function (sound) {
        return sound.param === param
      }).pad = (value === 0) ? false : value - 1
    }
  }

  function HandleMIDI (event) {
    var sound

    // non-note events are passed through
    if (event instanceof Note) {
      // notes outside DMD's range are NOT passed through
      sound = Sounds[event.pitch + PITCH_INPUT_OFFSET]
      if (!sound || sound.pad === false) return

      event.pitch = sound.pad + PITCH_OUTPUT_OFFSET
    }

    event.send()
  }

  // public methods
  return {
    ParameterChanged,
    HandleMIDI
  }
})()

// let Logic reign...
ParameterChanged = App.ParameterChanged
HandleMIDI = App.HandleMIDI
<commit_msg>(feat) Add map for 'Ace Clone FR-2L'<commit_after>/*eslint no-unused-vars: [2, "local"] */
/*global ParameterChanged:true, HandleMIDI:true,
  SetParameter, UpdatePluginParameters, MIDI, Note */

const MODELS = {
  'Ace Clone FR-2L': [
    ['BD', 'KICK'],
    ['SD', 'SNARE'],
    'CH',
    'OH',
    ['HT', 'PERC 3'],
    ['MT', 'PERC 2'],
    ['LT', 'PERC 1'],
    ['Cymbal', 'CRASH|RIDE'],
    ['Cowbell', 'RIM'],
    ['Clave', 'CLAP'],
    ['Electro Cowbell', 'FX'],
    ['LT2', 'PERC 3'],
    ['Bleep 1', 'FX'],
    ['Bleep 2', 'FX'],
    ['Buzz Synth', 'FX'],
    ['Acid Lead', 'FX']
  ],
}

// This object defines Drum Machine Designer's sound groups and
// each sound's pitch offset from C1. E.g. KICK 1 is C1+0 while
// KICK 2 is F2+17. It's used for a few things internally as
// well as specifying the groups' dropdown order in the UI.
const DRUMMER = {
  'KICK': [0, 17],
  'SNARE': [2, 4, 16],
  'RIM': [1],
  'CLAP': [3, 22],
  'PERC': [5, 7, 9, 11, 12],
  'CH': [6],
  'PH': [8],
  'OH': [10],
  'CRASH': [13],
  'RIDE': [15],
  'SHAKER': [18, 14],
  'FX': [19, 21, 23, 20]
}

const PITCH_INPUT_OFFSET = -36 // C1 will become array index 0
const PITCH_OUTPUT_OFFSET = 60 // C3

// begin UI with first two dropdowns
var PluginParameters = [{
  name: 'Model',
  type: 'menu',
  valueStrings: Object.keys(MODELS),
  defaultValue: 0
}, {
  name: 'Randomize',
  type: 'menu',
  defaultValue: 1,
  valueStrings: ['Reset default', '-', 'KICK', 'SNARE', 'CLAP', 'PERC', 'FX']
}]

var App = (function () {
  var model, modelName
  var Sounds = []
  var _initComplete
  var _skipUpdates

  // one-time init
  for (var group in DRUMMER) {
    DRUMMER[group].forEach(function (pitchOffset, alt) {
      // sound map dropdown placeholders prevent Logic
      // from complaining about param changes
      PluginParameters.push({
        name: group + (DRUMMER[group].length > 1 ? ' ' + (alt + 1) : ''),
        type: 'menu',
        defaultValue: 0,
        valueStrings: ['-'].concat(Array(16).fill(''))
      })

      // array of objects 0-indexed relative to C1's MIDI pitch
      Sounds[pitchOffset] = {
        group,
        alt,
        param: PluginParameters.length - 1, // current PluginParameter index
        pad: false // not mapped yet
      }
    })
  }

  /**
   * map a sound's pad using the specified method
   * @param  {[type]} sound  [description]
   * @param  {[type]} method [description]
   * @return {[type]}        [description]
   */
  function mapSound (sound, method) {
    if (Array.isArray(sound)) {
      sound.forEach(function (s) {
        mapSound(s, method)
      })

      return
    }

    var altOut = (function (altIn, group, method) {
      // if there's not much choice...
      if (model[group].length === 0) return false
      if (model[group].length === 1) return 0

      if (method === 'random') {
        return Math.floor(Math.random() * model[group].length)
      }

      // default (linear transform)
      return Math.floor(
        altIn / (DRUMMER[group].length - 1) * (model[group].length - 1)
      )
    })(sound.alt, sound.group, method)

    sound.pad = (altOut === false) ? false : model[sound.group][altOut]
    SetParameter(sound.param, (sound.pad === false) ? 0 : sound.pad + 1)
  }

  function setModel (key, setDefaults) {
    var opts = ['-']

    // avoid doing anything if we can
    if (key === modelName) return
    modelName = key

    // group model sounds like in DRUMMER
    model = {}
    for (var group in DRUMMER) {
      model[group] = []
    }

    // parse model definition
    MODELS[modelName].forEach(function (arr, pad) {
      // allow for arrr-ayless shorthand, matey
      if (!Array.isArray(arr)) arr = [arr, arr]

      // sound assignments
      arr[1].toUpperCase().split('|').forEach(function (txt) {
        var [group, num] = txt.split(' ')
        if (!group) return

        // set pad's index/position (0-15)
        if (num && typeof model[group][num - 1] === 'undefined') {
          model[group][num - 1] = pad
        } else {
          // add to next available alt slot
          model[group].push(pad)
        }
      })

      // add pad to dropdown options
      opts.push(arr[0] + '   [' + MIDI.noteName(pad + PITCH_OUTPUT_OFFSET) + ']')
    })

      // update pad selection/dropdowns for this model
    for (var sound in Sounds) {
      PluginParameters[Sounds[sound].param].valueStrings = opts
    }

    // set default mapping?
    if (setDefaults) mapSound(Sounds, 'default')

    // need Logic to update dropdown options but don't
    // care about the ParameterChanged() call for each
    _skipUpdates = true
    UpdatePluginParameters()
  }

  function ParameterChanged (param, value) {
    if (_initComplete && _skipUpdates) {
      if (param === PluginParameters.length - 1) {
        _skipUpdates = false // update sweep is complete
      }
      return
    }

    // when Logic has finished restoring params for the first time
    if (!_initComplete && param === PluginParameters.length - 1) {
      _initComplete = true
    }

    if (param === 0) {
      // change model and set defaults if user initiated change
      setModel(PluginParameters[param].valueStrings[value], _initComplete)
    } else if (param === 1) {
      // "Edit" dropdown
      var group = PluginParameters[param].valueStrings[value]

      if (value === 0) {
        mapSound(Sounds, 'default') // reset defaults
      } else if (value > 1) {
        // randomize group
        mapSound(Sounds.filter(function (sound) {
          return sound.group === group
        }), 'random')
      }

      // set dropdown back to '-'
      SetParameter(param, 1)
    } else {
      // find the sound with this param index
      Sounds.find(function (sound) {
        return sound.param === param
      }).pad = (value === 0) ? false : value - 1
    }
  }

  function HandleMIDI (event) {
    var sound

    // non-note events are passed through
    if (event instanceof Note) {
      // notes outside DMD's range are NOT passed through
      sound = Sounds[event.pitch + PITCH_INPUT_OFFSET]
      if (!sound || sound.pad === false) return

      event.pitch = sound.pad + PITCH_OUTPUT_OFFSET
    }

    event.send()
  }

  // public methods
  return {
    ParameterChanged,
    HandleMIDI
  }
})()

// let Logic reign...
ParameterChanged = App.ParameterChanged
HandleMIDI = App.HandleMIDI
<|endoftext|>


<commit_before>// ==UserScript==
// @name           sendToCgeo
// @namespace      http://send2.cgeo.org/
// @description    Add Send to c:geo button to geocaching.com
// @include        http://www.geocaching.com/seek/cache_details*
// @include        http://www.geocaching.com/map/beta*
// @version 	   0.24
// ==/UserScript==

// Inserts javascript that will be called by the s2cgeo button
var s       = document.createElement('script');
s.type      = 'text/javascript';
s.innerHTML =  'function s2cgeo(code) {'
                + 'window.open(\'http://send2.cgeo.org/add.html?cache=\'+code,'
                + '\'cgeo\',\'height=50,width=50\'); }';
document.getElementsByTagName("head")[0].appendChild(s);

var map = document.getElementById('cacheDetailsTemplate');

if( map != null )
{
    var html = 'Log Visit
 '
             + ''
             + ' '
             + 'Send to c:geo';

    map.innerHTML = map.innerHTML.replace('Log Visit', html);
}
else
{
    var d         = document.getElementById('Download');
    var m         = d.children;
    var last      = m.item(m.length-1);
    var GCElement = document.getElementById('ctl00_ContentBody_CoordInfoLinkControl1_uxCoordInfoCode');
    var GCCode    = GCElement.innerHTML;

    var html = '| <input type="button" '
             + 'name="ctl00$ContentBody$btnSendTocgeo" '
             + 'value="Send to c:geo" '
             + 'onclick="s2cgeo(\''+GCCode+'\'); '
             + 'return false;" '
             + 'id="ctl00_ContentBody_btnSendTocgeo" />';

    last.innerHTML = last.innerHTML + html;
}<commit_msg>Updated send2cgeo.user.js to adapt the map changes done by Groundspeak on 2012-02-14.<commit_after>// ==UserScript==
// @name           sendToCgeo
// @namespace      http://send2.cgeo.org/
// @description    Add Send to c:geo button to geocaching.com
// @include        http://www.geocaching.com/seek/cache_details*
// @include        http://www.geocaching.com/map/default*
// @version        0.25
// ==/UserScript==

// Inserts javascript that will be called by the s2cgeo button
var s       = document.createElement('script');
s.type      = 'text/javascript';
s.innerHTML =  'function s2cgeo(code) {'
                + 'window.open(\'http://send2.cgeo.org/add.html?cache=\'+code,'
                + '\'cgeo\',\'height=50,width=50\'); }';
document.getElementsByTagName("head")[0].appendChild(s);

var map = document.getElementById('cacheDetailsTemplate');

if( map != null )
{
    var html = 'Log Visit
 '
             + ''
             + ' '
             + 'Send to c:geo';

    map.innerHTML = map.innerHTML.replace('Log Visit', html);
}
else
{
    var d         = document.getElementById('Download');
    var m         = d.children;
    var last      = m.item(m.length-1);
    var GCElement = document.getElementById('ctl00_ContentBody_CoordInfoLinkControl1_uxCoordInfoCode');
    var GCCode    = GCElement.innerHTML;

    var html = '| <input type="button" '
             + 'name="ctl00$ContentBody$btnSendTocgeo" '
             + 'value="Send to c:geo" '
             + 'onclick="s2cgeo(\''+GCCode+'\'); '
             + 'return false;" '
             + 'id="ctl00_ContentBody_btnSendTocgeo" />';

    last.innerHTML = last.innerHTML + html;
}<|endoftext|>


<commit_before>/*    Copyright 2016 - 2020 Firewalla Inc
 *
 *    This program is free software: you can redistribute it and/or  modify
 *    it under the terms of the GNU Affero General Public License, version 3,
 *    as published by the Free Software Foundation.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
'use strict';

const log = require('../net2/logger.js')(__filename);
const Sensor = require('./Sensor.js').Sensor;
const Config = require('../net2/config.js');
const exec = require('child-process-promise').exec;
const { Rule } = require('../net2/Iptables.js');
const rclient = require('../util/redis_manager.js').getRedisClient();
const f = require('../net2/Firewalla.js');
const Tail = require('../vendor_lib/always-tail.js');
const LOG_PREFIX = "[FW_ACL_AUDIT]";
const {Address4, Address6} = require('ip-address');
const sysManager = require('../net2/SysManager.js');
const HostTool = require('../net2/HostTool.js');
const hostTool = new HostTool();
const DNSTool = require('../net2/DNSTool.js');
const dnsTool = new DNSTool();
const Message = require('../net2/Message.js');
const sem = require('./SensorEventManager.js').getInstance();

const auditLogFile = "/var/log/acl_audit.log";

const featureName = "acl_audit";

const auditDropCounterKey = "audit:drop:cnt";
const auditDropCounterExpireTime = 3600 * 24 * 14; // two weeks

class ACLAuditLogPlugin extends Sensor {
  async run() {
    this.hookFeature(featureName);
    this.timeoutTask = null;
    this.auditLogReader = null;
  }

  async job() {
    // ensure log file is readable
    await exec(`sudo touch ${auditLogFile}`).catch((err) => {});
    await exec(`sudo chgrp adm ${auditLogFile}`).catch((err) => {});
    await exec(`sudo chown syslog ${auditLogFile}`).catch((err) => {});
    await exec(`sudo chmod 644 ${auditLogFile}`).catch((err) => {});

    this.auditLogReader = new Tail(auditLogFile, '\n');
    if (this.auditLogReader != null) {
      this.auditLogReader.on('line', (line) => {
        this._processIptablesLog(line);
      });
      this.auditLogReader.on('error', (err) => {
        log.error("Error while reading acl audit log", err.message);
      })
    }

    sem.on(Message.MSG_ACL_DNS_NXDOMAIN, (message) => {
      if (message && message.record)
        this._processDnsNxdomainRecord(message.record);
    });
  }

  // Jul  2 16:35:57 firewalla kernel: [ 6780.606787] [FW_ACL_AUDIT]IN=br0 OUT=eth0 MAC=20:6d:31:fe:00:07:88:e9:fe:86:ff:94:08:00 SRC=192.168.210.191 DST=23.129.64.214 LEN=64 TOS=0x00 PREC=0x00 TTL=63 ID=0 DF PROTO=TCP SPT=63349 DPT=443 WINDOW=65535 RES=0x00 SYN URGP=0 MARK=0x87
  async _processIptablesLog(line) {
    const content = line.substring(line.indexOf(LOG_PREFIX) + LOG_PREFIX.length); // extract content after log prefix
    if (!content || content.length == 0)
      return;
    const params = content.split(' ');
    const record = {aclType: "ip"};
    for (const param of params) {
      const kvPair = param.split('=');
      if (kvPair.length !== 2)
        continue;
      const k = kvPair[0];
      const v = kvPair[1];
      switch (k) {
        case "SRC": {
          record.src = v;
          break;
        }
        case "DST": {
          record.dst = v;
          break;
        }
        case "PROTO": {
          record.protocol = v;
          break;
        }
        case "SPT": {
          record.sport = v;
          break;
        }
        case "DPT": {
          record.dport = v;
          break;
        }
        default:
      }
    }
    if (sysManager.isLocalIP(record.src)) {
      record.dir = 'out';
      const intf = new Address4(record.src).isValid() ? sysManager.getInterfaceViaIP4(record.src) : sysManager.getInterfaceViaIP6(record.src);
      // not able to map ip to unique identity from VPN yet
      if (!intf || intf.name === "tun_fwvpn")
        return;
      const mac = await hostTool.getMacByIPWithCache(record.src);
      if (mac) {
        if (!sysManager.isLocalIP(record.dst)) {
          const domain = await dnsTool.getDns(record.dst);
          if (domain)
            record.domain = domain;
        }
        const key = this._getAuditDropKey(mac);
        await rclient.zaddAsync(key, Date.now() / 1000, JSON.stringify(record));
      }
    } else {
      if (sysManager.isLocalIP(record.dst)) {
        record.dir = 'in';
        const intf = new Address4(record.dst).isValid() ? sysManager.getInterfaceViaIP4(record.dst) : sysManager.getInterfaceViaIP6(record.dst);
        // not able to map ip to unique identity from VPN yet
        if (!intf || intf.name === "tun_fwvpn")
          return;
        const mac = await hostTool.getMacByIPWithCache(record.dst);
        if (mac) {
          if (!sysManager.isLocalIP(record.src)) {
            const domain = await dnsTool.getDns(record.src);
            if (domain)
              record.domain = domain;
          }
          const key = this._getAuditDropKey(mac);
          await rclient.zaddAsync(key, Date.now() / 1000, JSON.stringify(record));
        }
      } else {
        record.dir = 'local';
      }
    }

    await this._recordCounter(record);
  }

  // dest could be a domain or ip
  async _recordCounter(record) {
    // invalid record if record.aclType is dns && record.domain doesn't exist
    if (record.aclType === "dns" && !record.domain) {
      return;
    }

    if (record.domain) {
      await rclient.zincrbyAsync(auditDropCounterKey, record.domain);
    } else {
      if (record.dir === 'out') {
        await rclient.zincrbyAsync(auditDropCounterKey, record.dst);
      }

      if (record.dir === 'in') {
        await rclient.zincrbyAsync(auditDropCounterKey, record.src);
      }
    }

    await rclient.expireAsync(auditDropCounterKey, auditDropCounterExpireTime);
  }

  async _rotateCounter() {
    // TBD
  }

  async _processDnsNxdomainRecord(record) {
    if (sysManager.isLocalIP(record.src)) {
      const intf = new Address4(record.src).isValid() ? sysManager.getInterfaceViaIP4(record.src) : sysManager.getInterfaceViaIP6(record.src);
      // not able to map ip to unique identity from VPN yet
      if (!intf || intf.name === "tun_fwvpn")
        return;
      const mac = await hostTool.getMacByIPWithCache(record.src);
      if (mac) {
        record.aclType = "dns";
        const key = this._getAuditDropKey(mac);
        await rclient.zaddAsync(key, Date.now() / 1000, JSON.stringify(record));
        await this._recordCounter(record);
      }
    }
  }

  _getAuditDropKey(mac) {
    return `audit:drop:${mac}`;
  }

  async globalOn() {
    // create rsyslog config that filters iptables log to specific syslog file
    await exec(`sudo cp ${f.getFirewallaHome()}/etc/rsyslog.d/30-acl-audit.conf /etc/rsyslog.d/`).then(() => exec(`sudo systemctl restart rsyslog`)).catch((err) => {});
    const rule = new Rule("filter").chn("FW_DROP").jmp(`LOG --log-prefix "${LOG_PREFIX}"`);
    const rule6 = rule.clone().fam(6);
    await exec(rule.toCmd('-I')).catch((err) => {
      log.error("Failed to enable IPv4 iptables drop log", err.message);
    });
    await exec(rule6.toCmd('-I')).catch((err) => {
      log.error("Failed to enable IPv6 iptables drop log", err.message);
    });
    // in consideration of performance, the audit log will be automatically disabled in 30 minutes
    if (this.timeoutTask)
      clearTimeout(this.timeoutTask);
    this.timeoutTask = setTimeout(() => {

      // do not auto disable when in dev branch
      if (f.isDevelopmentVersion()) {
        return;
      }

      Config.disableDynamicFeature(featureName);
    }, 30 * 60 * 1000);
  }

  async globalOff() {
    const rule = new Rule("filter").chn("FW_DROP").jmp(`LOG --log-prefix "${LOG_PREFIX}"`);
    const rule6 = rule.clone().fam(6);
    await exec(rule.toCmd('-D')).catch((err) => {
      log.error("Failed to disable IPv4 iptables drop log", err.message);
    });
    await exec(rule6.toCmd('-D')).catch((err) => {
      log.error("Failed to disable IPv6 iptables drop log", err.message);
    });
    // remove rsyslog config that filters iptables log to specific syslog file
    await exec(`sudo rm /etc/rsyslog.d/30-acl-audit.conf`).then(() => exec(`sudo systemctl restart rsyslog`)).catch((err) => {});
    if (this.timeoutTask)
      clearTimeout(this.timeoutTask);
  }

}

module.exports = ACLAuditLogPlugin;<commit_msg>update<commit_after>/*    Copyright 2016 - 2020 Firewalla Inc
 *
 *    This program is free software: you can redistribute it and/or  modify
 *    it under the terms of the GNU Affero General Public License, version 3,
 *    as published by the Free Software Foundation.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
'use strict';

const log = require('../net2/logger.js')(__filename);
const Sensor = require('./Sensor.js').Sensor;
const Config = require('../net2/config.js');
const exec = require('child-process-promise').exec;
const { Rule } = require('../net2/Iptables.js');
const rclient = require('../util/redis_manager.js').getRedisClient();
const f = require('../net2/Firewalla.js');
const Tail = require('../vendor_lib/always-tail.js');
const LOG_PREFIX = "[FW_ACL_AUDIT]";
const {Address4, Address6} = require('ip-address');
const sysManager = require('../net2/SysManager.js');
const HostTool = require('../net2/HostTool.js');
const hostTool = new HostTool();
const DNSTool = require('../net2/DNSTool.js');
const dnsTool = new DNSTool();
const Message = require('../net2/Message.js');
const sem = require('./SensorEventManager.js').getInstance();

const auditLogFile = "/var/log/acl_audit.log";

const featureName = "acl_audit";

const auditDropCounterKey = "audit:drop:cnt";
const auditDropCounterExpireTime = 3600 * 24 * 7; // one week

class ACLAuditLogPlugin extends Sensor {
  async run() {
    this.hookFeature(featureName);
    this.timeoutTask = null;
    this.auditLogReader = null;
  }

  async job() {
    // ensure log file is readable
    await exec(`sudo touch ${auditLogFile}`).catch((err) => {});
    await exec(`sudo chgrp adm ${auditLogFile}`).catch((err) => {});
    await exec(`sudo chown syslog ${auditLogFile}`).catch((err) => {});
    await exec(`sudo chmod 644 ${auditLogFile}`).catch((err) => {});

    this.auditLogReader = new Tail(auditLogFile, '\n');
    if (this.auditLogReader != null) {
      this.auditLogReader.on('line', (line) => {
        this._processIptablesLog(line);
      });
      this.auditLogReader.on('error', (err) => {
        log.error("Error while reading acl audit log", err.message);
      })
    }

    sem.on(Message.MSG_ACL_DNS_NXDOMAIN, (message) => {
      if (message && message.record)
        this._processDnsNxdomainRecord(message.record);
    });
  }

  // Jul  2 16:35:57 firewalla kernel: [ 6780.606787] [FW_ACL_AUDIT]IN=br0 OUT=eth0 MAC=20:6d:31:fe:00:07:88:e9:fe:86:ff:94:08:00 SRC=192.168.210.191 DST=23.129.64.214 LEN=64 TOS=0x00 PREC=0x00 TTL=63 ID=0 DF PROTO=TCP SPT=63349 DPT=443 WINDOW=65535 RES=0x00 SYN URGP=0 MARK=0x87
  async _processIptablesLog(line) {
    const content = line.substring(line.indexOf(LOG_PREFIX) + LOG_PREFIX.length); // extract content after log prefix
    if (!content || content.length == 0)
      return;
    const params = content.split(' ');
    const record = {aclType: "ip"};
    for (const param of params) {
      const kvPair = param.split('=');
      if (kvPair.length !== 2)
        continue;
      const k = kvPair[0];
      const v = kvPair[1];
      switch (k) {
        case "SRC": {
          record.src = v;
          break;
        }
        case "DST": {
          record.dst = v;
          break;
        }
        case "PROTO": {
          record.protocol = v;
          break;
        }
        case "SPT": {
          record.sport = v;
          break;
        }
        case "DPT": {
          record.dport = v;
          break;
        }
        default:
      }
    }
    if (sysManager.isLocalIP(record.src)) {
      record.dir = 'out';
      const intf = new Address4(record.src).isValid() ? sysManager.getInterfaceViaIP4(record.src) : sysManager.getInterfaceViaIP6(record.src);
      // not able to map ip to unique identity from VPN yet
      if (!intf || intf.name === "tun_fwvpn")
        return;
      const mac = await hostTool.getMacByIPWithCache(record.src);
      if (mac) {
        if (!sysManager.isLocalIP(record.dst)) {
          const domain = await dnsTool.getDns(record.dst);
          if (domain)
            record.domain = domain;
        }
        const key = this._getAuditDropKey(mac);
        await rclient.zaddAsync(key, Date.now() / 1000, JSON.stringify(record));
      }
    } else {
      if (sysManager.isLocalIP(record.dst)) {
        record.dir = 'in';
        const intf = new Address4(record.dst).isValid() ? sysManager.getInterfaceViaIP4(record.dst) : sysManager.getInterfaceViaIP6(record.dst);
        // not able to map ip to unique identity from VPN yet
        if (!intf || intf.name === "tun_fwvpn")
          return;
        const mac = await hostTool.getMacByIPWithCache(record.dst);
        if (mac) {
          if (!sysManager.isLocalIP(record.src)) {
            const domain = await dnsTool.getDns(record.src);
            if (domain)
              record.domain = domain;
          }
          const key = this._getAuditDropKey(mac);
          await rclient.zaddAsync(key, Date.now() / 1000, JSON.stringify(record));
        }
      } else {
        record.dir = 'local';
      }
    }

    await this._recordCounter(record);
  }

  // dest could be a domain or ip
  async _recordCounter(record) {
    // invalid record if record.aclType is dns && record.domain doesn't exist
    if (record.aclType === "dns" && !record.domain) {
      return;
    }

    if (record.domain) {
      await rclient.zincrbyAsync(auditDropCounterKey, record.domain);
    } else {
      if (record.dir === 'out') {
        await rclient.zincrbyAsync(auditDropCounterKey, record.dst);
      }

      if (record.dir === 'in') {
        await rclient.zincrbyAsync(auditDropCounterKey, record.src);
      }
    }

    await rclient.expireAsync(auditDropCounterKey, auditDropCounterExpireTime);
  }

  async _rotateCounter() {
    // TBD
  }

  async _processDnsNxdomainRecord(record) {
    if (sysManager.isLocalIP(record.src)) {
      const intf = new Address4(record.src).isValid() ? sysManager.getInterfaceViaIP4(record.src) : sysManager.getInterfaceViaIP6(record.src);
      // not able to map ip to unique identity from VPN yet
      if (!intf || intf.name === "tun_fwvpn")
        return;
      const mac = await hostTool.getMacByIPWithCache(record.src);
      if (mac) {
        record.aclType = "dns";
        const key = this._getAuditDropKey(mac);
        await rclient.zaddAsync(key, Date.now() / 1000, JSON.stringify(record));
        await this._recordCounter(record);
      }
    }
  }

  _getAuditDropKey(mac) {
    return `audit:drop:${mac}`;
  }

  async globalOn() {
    // create rsyslog config that filters iptables log to specific syslog file
    await exec(`sudo cp ${f.getFirewallaHome()}/etc/rsyslog.d/30-acl-audit.conf /etc/rsyslog.d/`).then(() => exec(`sudo systemctl restart rsyslog`)).catch((err) => {});
    const rule = new Rule("filter").chn("FW_DROP").jmp(`LOG --log-prefix "${LOG_PREFIX}"`);
    const rule6 = rule.clone().fam(6);
    await exec(rule.toCmd('-I')).catch((err) => {
      log.error("Failed to enable IPv4 iptables drop log", err.message);
    });
    await exec(rule6.toCmd('-I')).catch((err) => {
      log.error("Failed to enable IPv6 iptables drop log", err.message);
    });
    // in consideration of performance, the audit log will be automatically disabled in 30 minutes
    if (this.timeoutTask)
      clearTimeout(this.timeoutTask);
    this.timeoutTask = setTimeout(() => {

      // do not auto disable when in dev branch
      if (f.isDevelopmentVersion()) {
        return;
      }

      Config.disableDynamicFeature(featureName);
    }, 30 * 60 * 1000);
  }

  async globalOff() {
    const rule = new Rule("filter").chn("FW_DROP").jmp(`LOG --log-prefix "${LOG_PREFIX}"`);
    const rule6 = rule.clone().fam(6);
    await exec(rule.toCmd('-D')).catch((err) => {
      log.error("Failed to disable IPv4 iptables drop log", err.message);
    });
    await exec(rule6.toCmd('-D')).catch((err) => {
      log.error("Failed to disable IPv6 iptables drop log", err.message);
    });
    // remove rsyslog config that filters iptables log to specific syslog file
    await exec(`sudo rm /etc/rsyslog.d/30-acl-audit.conf`).then(() => exec(`sudo systemctl restart rsyslog`)).catch((err) => {});
    if (this.timeoutTask)
      clearTimeout(this.timeoutTask);
  }

}

module.exports = ACLAuditLogPlugin;<|endoftext|>


<commit_before>module.exports.createDeviceOnOffJobs = function(device, ruleDeviceOn, ruleDeviceOff){
  //Schedule device on job
  ruleDeviceOn.minute = device.onTime.minute;
  ruleDeviceOn.hour = device.onTime.hour;
  schedule.scheduleJob(ruleDeviceOn, function(){
    console.log('Aquarium Lights - ' + new Date() + ' ' + device.name + ' turned on');
    device.gpio.writeSync(ON);
  });

  //Schedule device off job
  ruleDeviceOff.minute = device.offTime.minute;
  ruleDeviceOff.hour = device.offTime.hour;
  schedule.scheduleJob(ruleDeviceOff, function(){
    console.log('Aquarium Lights - ' + new Date() + ' ' + device.name + ' turned off');
    device.gpio.writeSync(OFF);
  });

  console.log('Aquarium Lights - Jobs for ' + device.name + ' scheduled');
};
<commit_msg>create devicejob.js<commit_after>var schedule = require('node-schedule');

module.exports.createDeviceOnOffJobs = function(device, ruleDeviceOn, ruleDeviceOff){
  //Schedule device on job
  ruleDeviceOn.minute = device.onTime.minute;
  ruleDeviceOn.hour = device.onTime.hour;
  schedule.scheduleJob(ruleDeviceOn, function(){
    console.log('Aquarium Lights - ' + new Date() + ' ' + device.name + ' turned on');
    device.gpio.writeSync(ON);
  });

  //Schedule device off job
  ruleDeviceOff.minute = device.offTime.minute;
  ruleDeviceOff.hour = device.offTime.hour;
  schedule.scheduleJob(ruleDeviceOff, function(){
    console.log('Aquarium Lights - ' + new Date() + ' ' + device.name + ' turned off');
    device.gpio.writeSync(OFF);
  });

  console.log('Aquarium Lights - Jobs for ' + device.name + ' scheduled');
};
<|endoftext|>


<commit_before>var util = require('util'),
    connect = require('connect'),
    port = 9000;

connect.createServer(connect.static(__dirname)).listen(process.env.PORT || port);
util.puts('Listening on ' + (process.env.PORT || port) + '...');
util.puts('Press Ctrl + C to stop.');<commit_msg>Update server to use connect serve-static<commit_after>var util = require('util');
var connect = require('connect');
var serveStatic = require('serve-static');
var path = require('path');

var port = 9000;
var app = connect();

// parent folder
app.use(serveStatic(path.resolve(__dirname)));

// Listen
app.listen(process.env.PORT || port);
util.puts('Listening on ' + (process.env.PORT || port) + '...');
util.puts('Press Ctrl + C to stop.');<|endoftext|>


<commit_before>const spawn = require('child_process').spawn;
const fs = require("fs");
const webpack = require('webpack');
const WebpackDev = require('webpack-dev-server');
const config = require('./webpack.config.dev.js');
const httpProxy = require('http-proxy');
const path = require('path');

const loadSettings = function () {
  let fileName = './settings.json.default.json';
  if (fs.existsSync('./settings.json')) {
    fileName = './settings.json';
  }

  return require(fileName).couchserver || {
    port: process.env.FAUXTON_PORT || 8000,
    contentSecurityPolicy: true,
    proxy: {
      target: process.env.COUCH_HOST || 'http://127.0.0.1:5984',
      changeOrigin: false
    }
  };
};

const settings = loadSettings();

const devSetup = function (cb) {
  console.log('setup dev environment');
  const cmd = 'devSetupWithClean';
  if (settings.noClean) {
    cmd = 'devSetup';
  }

  const grunt = spawn('grunt', [cmd]);

  grunt.stdout.on('data', (data) => {
    console.log(data.toString());
  });

  grunt.stderr.on('error', (data) => {
    console.log('Setup error:', data.toString());
  });

  grunt.on('close', (code) => {
    console.log('dev setup finished with code', code);
    if (code === 0) {
      cb();
    }
  });
};

const defaultHeaderValue = "default-src 'self'; child-src 'self' blob:; img-src 'self' data:; font-src 'self'; " +
                  "script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline';";
function getCspHeaders () {
  if (!settings.contentSecurityPolicy) {
    return;
  }

  const cspHeader = settings.contentSecurityPolicyHeader || defaultHeaderValue;

  return {
    'Content-Security-Policy': cspHeader
  };
};

const runWebpackServer = function () {
  const proxy = httpProxy.createServer({
    secure: false,
    changeOrigin: true,
    target: settings.proxy.target
  });

  proxy.on('proxyRes', function (proxyRes) {
    if (proxyRes.headers['set-cookie']) {
      proxyRes.headers['set-cookie'][0] = proxyRes.headers["set-cookie"][0].replace('Secure', '');
    }
  });

  proxy.on('error', function () {
    // don't explode on cancelled requests
  });

  const options = {
    contentBase: path.join(__dirname, '/dist/debug/'),
    host: 'localhost',
    port: process.env.FAUXTON_PORT || 8000,
    overlay: true,
    hot: false,
    historyApiFallback: false,
    stats: {
      colors: true,
    },
    headers: getCspHeaders(),
    setup: (app) => {
      app.all('*', (req, res, next) => {
        const accept = req.headers.accept ? req.headers.accept.split(',') : '';

        if (/application\/json/.test(accept[0])) {
          proxy.web(req, res);
          return;
        }

        next();
      });
    }
  };

  const compiler = webpack(config);
  const server = new WebpackDev(compiler, options);

  server.listen(options.port, '0.0.0.0', function (err) {
    if (err) {
      console.log(err);
      return;
    }
    console.log('listening on', options.host, options.port);
    console.log('Starting first compile. This will take about 10 seconds...');
  });
};

devSetup(runWebpackServer);
<commit_msg>Fix const error in devserver<commit_after>const spawn = require('child_process').spawn;
const fs = require("fs");
const webpack = require('webpack');
const WebpackDev = require('webpack-dev-server');
const config = require('./webpack.config.dev.js');
const httpProxy = require('http-proxy');
const path = require('path');

const loadSettings = function () {
  let fileName = './settings.json.default.json';
  if (fs.existsSync('./settings.json')) {
    fileName = './settings.json';
  }

  return require(fileName).couchserver || {
    port: process.env.FAUXTON_PORT || 8000,
    contentSecurityPolicy: true,
    proxy: {
      target: process.env.COUCH_HOST || 'http://127.0.0.1:5984',
      changeOrigin: false
    }
  };
};

const settings = loadSettings();

const devSetup = function (cb) {
  console.log('setup dev environment');
  let cmd = 'devSetupWithClean';
  if (settings.noClean) {
    cmd = 'devSetup';
  }

  const grunt = spawn('grunt', [cmd]);

  grunt.stdout.on('data', (data) => {
    console.log(data.toString());
  });

  grunt.stderr.on('error', (data) => {
    console.log('Setup error:', data.toString());
  });

  grunt.on('close', (code) => {
    console.log('dev setup finished with code', code);
    if (code === 0) {
      cb();
    }
  });
};

const defaultHeaderValue = "default-src 'self'; child-src 'self' blob:; img-src 'self' data:; font-src 'self'; " +
                  "script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline';";
function getCspHeaders () {
  if (!settings.contentSecurityPolicy) {
    return;
  }

  const cspHeader = settings.contentSecurityPolicyHeader || defaultHeaderValue;

  return {
    'Content-Security-Policy': cspHeader
  };
};

const runWebpackServer = function () {
  const proxy = httpProxy.createServer({
    secure: false,
    changeOrigin: true,
    target: settings.proxy.target
  });

  proxy.on('proxyRes', function (proxyRes) {
    if (proxyRes.headers['set-cookie']) {
      proxyRes.headers['set-cookie'][0] = proxyRes.headers["set-cookie"][0].replace('Secure', '');
    }
  });

  proxy.on('error', function () {
    // don't explode on cancelled requests
  });

  const options = {
    contentBase: path.join(__dirname, '/dist/debug/'),
    host: 'localhost',
    port: process.env.FAUXTON_PORT || 8000,
    overlay: true,
    hot: false,
    historyApiFallback: false,
    stats: {
      colors: true,
    },
    headers: getCspHeaders(),
    setup: (app) => {
      app.all('*', (req, res, next) => {
        const accept = req.headers.accept ? req.headers.accept.split(',') : '';

        if (/application\/json/.test(accept[0])) {
          proxy.web(req, res);
          return;
        }

        next();
      });
    }
  };

  const compiler = webpack(config);
  const server = new WebpackDev(compiler, options);

  server.listen(options.port, '0.0.0.0', function (err) {
    if (err) {
      console.log(err);
      return;
    }
    console.log('listening on', options.host, options.port);
    console.log('Starting first compile. This will take about 10 seconds...');
  });
};

devSetup(runWebpackServer);
<|endoftext|>


<commit_before>jsio('from shared.javascript import Class, bind')
jsio('import fan.ui.Component')

exports = Class(fan.ui.Component, function(supr) {

	this._className = 'KeyboardFocus'
	this._borderWidth = 7

	this.init = function() {
		supr(this, 'init')
		this._on(document, 'keydown', bind(this, '_onKeyDown'))

		this._focusIndex = -1
		this._panelIndex = -1
		this._keyMap = {}

		var keys = this.keys,
			keyMap = this._keyMap

		keyMap[keys['j']] = bind(this, '_moveFocus', 1)
		keyMap[keys['k']] = bind(this, '_moveFocus', -1)
		keyMap[keys['up arrow']] = this._keyMap[keys['j']]
		keyMap[keys['down arrow']] = this._keyMap[keys['k']]
		keyMap[keys['enter']] = bind(this, '_selectFocusedItem'),
		keyMap[keys['tab']] = bind(this, '_movePanel', 1)
		keyMap[keys['`']] = bind(this, '_movePanel', -1)
	}

	this.grabFocus = function(uiComponent) { this._focusedUIComponent = uiComponent }
	this.releaseFocus = function(uiComponent) {
		if (uiComponent != this._focusedUIComponent) { return }
		delete this._focusedUIComponent
	}

	this._onKeyDown = function(e) {
		if (this._focusedUIComponent) { return }
		var callback = this._keyMap[e.keyCode]
		if (callback) {
			e.cancel()
			callback()
		}
	}

	this._movePanel = function(steps) {
		var newPanelIndex = this._panelIndex + steps
		if (newPanelIndex < 0 || newPanelIndex > gPanels.length) { return }
		this._focusIndex = -1
		this._panelIndex = newPanelIndex
		this._moveFocus(1)
	}

	this._moveFocus = function(steps) {
		var panel = gPanels[this._panelIndex];
		if (!panel) { return }

		var newFocusIndex = this._focusIndex + steps,
			targets = panel.getElement().getElementsByClassName('fan-focusable')

		if (newFocusIndex < 0 || newFocusIndex >= targets.length) { return; }
		this._focusIndex = newFocusIndex;
		this._showAt(targets[newFocusIndex]);
	}

	this._selectFocusedItem = function() {
		logger.warn("TODO - notify focused item it was selected - equivelant of a click")
	}

	this._createContent = function() {
		var borderWidth = this._borderWidth

		this._top = this._create({ className: 'piece top', parent: this._element })
		this._left = this._create({ className: 'piece left', parent: this._element })
		this._right = this._create({ className: 'piece right', parent: this._element })
		this._bottom = this._create({ className: 'piece bottom', parent: this._element })

		this._left.style.width = this._right.style.width = borderWidth + 'px'
		this._top.style.height = this._bottom.style.height = borderWidth + 'px'
	}

	this._showAt = function(targetEl) {
		var focusPadding = 2,
			borderWidth = this._borderWidth,
			layout = this.getLayout(targetEl)

		document.body.appendChild(this.getElement())

		layout.width += focusPadding * 2;
		layout.height += focusPadding * 2;
		layout.left -= focusPadding;
		layout.top -= focusPadding;

		this.layout(this._top, { top: layout.top - borderWidth, left: layout.left - borderWidth,
			width: layout.width + borderWidth });
		this.layout(this._bottom, { top: layout.top + layout.height, left: layout.left - borderWidth,
			width: layout.width + borderWidth });
		this.layout(this._left, { top: layout.top, left: layout.left - borderWidth, height: layout.height });
		this.layout(this._right, { top: layout.top, left: layout.left + layout.width - borderWidth,
			height: layout.height });
	}
})<commit_msg>Don't let the panel index go above the length-1 of the number of panels<commit_after>jsio('from shared.javascript import Class, bind')
jsio('import fan.ui.Component')

exports = Class(fan.ui.Component, function(supr) {

	this._className = 'KeyboardFocus'
	this._borderWidth = 7

	this.init = function() {
		supr(this, 'init')
		this._on(document, 'keydown', bind(this, '_onKeyDown'))

		this._focusIndex = -1
		this._panelIndex = -1
		this._keyMap = {}

		var keys = this.keys,
			keyMap = this._keyMap

		keyMap[keys['j']] = bind(this, '_moveFocus', 1)
		keyMap[keys['k']] = bind(this, '_moveFocus', -1)
		keyMap[keys['up arrow']] = this._keyMap[keys['j']]
		keyMap[keys['down arrow']] = this._keyMap[keys['k']]
		keyMap[keys['enter']] = bind(this, '_selectFocusedItem'),
		keyMap[keys['tab']] = bind(this, '_movePanel', 1)
		keyMap[keys['`']] = bind(this, '_movePanel', -1)
	}

	this.grabFocus = function(uiComponent) { this._focusedUIComponent = uiComponent }
	this.releaseFocus = function(uiComponent) {
		if (uiComponent != this._focusedUIComponent) { return }
		delete this._focusedUIComponent
	}

	this._onKeyDown = function(e) {
		if (this._focusedUIComponent) { return }
		var callback = this._keyMap[e.keyCode]
		if (callback) {
			e.cancel()
			callback()
		}
	}

	this._movePanel = function(steps) {
		var newPanelIndex = this._panelIndex + steps
		if (newPanelIndex < 0 || newPanelIndex >= gPanels.length) { return }
		this._focusIndex = -1
		this._panelIndex = newPanelIndex
		this._moveFocus(1)
	}

	this._moveFocus = function(steps) {
		var panel = gPanels[this._panelIndex];
		if (!panel) { return }

		var newFocusIndex = this._focusIndex + steps,
			targets = panel.getElement().getElementsByClassName('fan-focusable')

		if (newFocusIndex < 0 || newFocusIndex >= targets.length) { return; }
		this._focusIndex = newFocusIndex;
		this._showAt(targets[newFocusIndex]);
	}

	this._selectFocusedItem = function() {
		logger.warn("TODO - notify focused item it was selected - equivelant of a click")
	}

	this._createContent = function() {
		var borderWidth = this._borderWidth

		this._top = this._create({ className: 'piece top', parent: this._element })
		this._left = this._create({ className: 'piece left', parent: this._element })
		this._right = this._create({ className: 'piece right', parent: this._element })
		this._bottom = this._create({ className: 'piece bottom', parent: this._element })

		this._left.style.width = this._right.style.width = borderWidth + 'px'
		this._top.style.height = this._bottom.style.height = borderWidth + 'px'
	}

	this._showAt = function(targetEl) {
		var focusPadding = 2,
			borderWidth = this._borderWidth,
			layout = this.getLayout(targetEl)

		document.body.appendChild(this.getElement())

		layout.width += focusPadding * 2;
		layout.height += focusPadding * 2;
		layout.left -= focusPadding;
		layout.top -= focusPadding;

		this.layout(this._top, { top: layout.top - borderWidth, left: layout.left - borderWidth,
			width: layout.width + borderWidth });
		this.layout(this._bottom, { top: layout.top + layout.height, left: layout.left - borderWidth,
			width: layout.width + borderWidth });
		this.layout(this._left, { top: layout.top, left: layout.left - borderWidth, height: layout.height });
		this.layout(this._right, { top: layout.top, left: layout.left + layout.width - borderWidth,
			height: layout.height });
	}
})<|endoftext|>


<commit_before>'use strict';

const fs = require('fs-extra');
const ngc = require('@angular/compiler-cli/src/main').main;
const librarianUtils = require('angular-librarian/commands/utilities');
const path = require('path');

const copyGlobs = require('./copy-globs');
const copyToBuild = require('./copy-build');
const inlineResources = require('./inline-resources');
const rollup = require('./rollup');

const colorize = librarianUtils.colorize;
const rootDir = path.resolve(__dirname, '..');
const buildDir = path.resolve(rootDir, 'build');
const distDir = path.resolve(rootDir, 'dist');
const libName = require(path.resolve(rootDir, 'package.json')).name;
const srcDir = path.resolve(rootDir, 'src');
const tscDir = path.resolve(rootDir, 'out-tsc');
const es5Dir = path.resolve(tscDir, 'lib-es5');
const es2015Dir = path.resolve(tscDir, 'lib-es2015');

const runPromise = (message, fn) => {
    return function() {
        console.info(colorize(message, 'cyan'));
        return fn().then(complete);
    };
};

const complete = (depth = 0) => {
    const spaces = ' '.repeat(depth);
    console.info(colorize(`${ spaces }> Complete`, 'green'));
};
const compileCode = () => Promise.all([2015, 5].map((type) =>
    ngc({ project: path.resolve(rootDir, `tsconfig.es${ type }.json`)})
        .then((exitCode) =>
            exitCode === 0 ? Promise.resolve() : Promise.reject()
        )
));
const copyMetadata = () =>
    copyGlobs(['**/*.d.ts', '**/*.metadata.json'], es2015Dir, distDir);
const copyPackageFiles = () =>
    copyGlobs(['.npmignore', 'package.json', 'README.md'], rootDir, distDir)
        .then(() => {
            const contents = fs.readFileSync(path.resolve(distDir, 'package.json'), 'utf8');

            return fs.writeFileSync(path.resolve(distDir, 'package.json'),  contents.replace('"dependencies":', '"peerDependencies":'));
        });
const copySource = () => copyGlobs('**/*', srcDir, buildDir);
const doInlining = () => inlineResources(buildDir, 'src');
const rollupBundles = () => rollup(libName, {
    dist: distDir,
    es2015: es2015Dir,
    es5: es5Dir,
    root: rootDir
});

return Promise.resolve()
    .then(runPromise('Copying `src` files into `build`', copySource))
    .then(runPromise('Inlining resources', doInlining))
    .then(runPromise('Compiling code', compileCode))
    .then(runPromise('Copying typings + metadata to `dist`', copyMetadata))
    .then(runPromise('Generating bundles via rollup', rollupBundles))
    .then(runPromise('Copying package files to `dist`', copyPackageFiles))
    .catch((error) => {
        console.error('\x1b[31m%s\x1b[0m', '> Build failed\n');
        console.error(error);
        process.exit(1);
    });
<commit_msg>Change build.js to use correct colorize method<commit_after>'use strict';

const fs = require('fs-extra');
const ngc = require('@angular/compiler-cli/src/main').main;
const librarianUtils = require('angular-librarian/commands/utilities');
const path = require('path');

const copyGlobs = require('./copy-globs');
const copyToBuild = require('./copy-build');
const inlineResources = require('./inline-resources');
const rollup = require('./rollup');

const colorize = librarianUtils.colorize;
const rootDir = path.resolve(__dirname, '..');
const buildDir = path.resolve(rootDir, 'build');
const distDir = path.resolve(rootDir, 'dist');
const libName = require(path.resolve(rootDir, 'package.json')).name;
const srcDir = path.resolve(rootDir, 'src');
const tscDir = path.resolve(rootDir, 'out-tsc');
const es5Dir = path.resolve(tscDir, 'lib-es5');
const es2015Dir = path.resolve(tscDir, 'lib-es2015');

const runPromise = (message, fn) => {
    return function() {
        console.info(colorize.colorize(message, 'cyan'));
        return fn().then(complete);
    };
};

const complete = (depth = 0) => {
    const spaces = ' '.repeat(depth);
    console.info(colorize.colorize(`${ spaces }> Complete`, 'green'));
};
const compileCode = () => Promise.all([2015, 5].map((type) =>
    ngc({ project: path.resolve(rootDir, `tsconfig.es${ type }.json`)})
        .then((exitCode) =>
            exitCode === 0 ? Promise.resolve() : Promise.reject()
        )
));
const copyMetadata = () =>
    copyGlobs(['**/*.d.ts', '**/*.metadata.json'], es2015Dir, distDir);
const copyPackageFiles = () =>
    copyGlobs(['.npmignore', 'package.json', 'README.md'], rootDir, distDir)
        .then(() => {
            const contents = fs.readFileSync(path.resolve(distDir, 'package.json'), 'utf8');

            return fs.writeFileSync(path.resolve(distDir, 'package.json'),  contents.replace('"dependencies":', '"peerDependencies":'));
        });
const copySource = () => copyGlobs('**/*', srcDir, buildDir);
const doInlining = () => inlineResources(buildDir, 'src');
const rollupBundles = () => rollup(libName, {
    dist: distDir,
    es2015: es2015Dir,
    es5: es5Dir,
    root: rootDir
});

return Promise.resolve()
    .then(runPromise('Copying `src` files into `build`', copySource))
    .then(runPromise('Inlining resources', doInlining))
    .then(runPromise('Compiling code', compileCode))
    .then(runPromise('Copying typings + metadata to `dist`', copyMetadata))
    .then(runPromise('Generating bundles via rollup', rollupBundles))
    .then(runPromise('Copying package files to `dist`', copyPackageFiles))
    .catch((error) => {
        console.error('\x1b[31m%s\x1b[0m', '> Build failed\n');
        console.error(error);
        process.exit(1);
    });
<|endoftext|>


<commit_before>import Ember from 'ember';

export default Ember.Component.extend({
  id: Ember.computed(function() {
      return this.get('widgetSettings').institution_id;
  }),
  institution: Ember.computed(function() {
      var id = this.get('institution_id');
      // look up institution model
      return {
         'id': id,
         'name': 'UC San Diego',
         'description': 'Data from the UC San Diego research community in the SHARE catalog.',
         'dashboards': []
      }
  })
});
<commit_msg>Fix about url param<commit_after>import Ember from 'ember';

export default Ember.Component.extend({
  institution: Ember.computed(function() {
      var id = this.get('widgetSettings').institution_id;
      // look up institution model
      return {
         'id': id,
         'name': 'UC San Diego',
         'description': 'Data from the UC San Diego research community in the SHARE catalog.',
         'dashboards': []
      }
  })
});
<|endoftext|>


<commit_before>'use strict';
var app = angular.module('com.module.dwFeeds');

app.controller('FeedsCtrl', function($scope, $state, $stateParams, DwDomain, DwTrail, DwTeam, DwFeed, DwServiceType, FeedsService, gettextCatalog, AppAuth) {

    //Put the currentUser in $scope for convenience
    $scope.currentUser = AppAuth.currentUser;
    $scope.feed = {};
    $scope.plProtocols = [
        {"name": "http","description": "http://"},
        {"name": "https","description": "https://" }
    ];
    $scope.plServiceTypes = [];
    $scope.plDomains = [];
    $scope.plTrails = [];
    $scope.plTeams = [];

    $scope.formFields = [{
        key: 'id',
        type: 'input',
        templateOptions: {
            label: gettextCatalog.getString('id'),
            disabled: true
        }
    },{
        key: 'name',
        type: 'input',
        templateOptions: {
            label: gettextCatalog.getString('Name'),
            required: true
        }
    }, {
        key: 'dwServiceTypeId',
        type: 'select',
        templateOptions: {
            label: gettextCatalog.getString('ServiceType'),
            options: $scope.plServiceTypes,
            valueProp: 'id',
            labelProp: 'name',
            required: true,
            disabled: false
        }
    }, {
        key: 'protocol',
        type: 'select',
        templateOptions: {
            label: gettextCatalog.getString('Protocol'),
            options: $scope.plProtocols,
            valueProp: 'name',
            labelProp: 'name',
            required: true,
            disabled: false
        }
    }, {
        key: 'feedUrl',
        type: 'input',
        templateOptions: {
            label: gettextCatalog.getString('Url'),
            required: true
        }
    }, {
        key: 'port',
        type: 'input',
        templateOptions: {
            label: gettextCatalog.getString('Port'),
            required: false
        }
    }, {
        key: 'index',
        type: 'input',
        templateOptions: {
            label: gettextCatalog.getString('Index'),
            required: false
        }
    }, {
        key: 'credentials',
        type: 'input',
        templateOptions: {
            label: gettextCatalog.getString('Credentials'),
            required: false
        }
    }, {
        key: 'dwTeams',
        type: 'multiCheckbox',
        templateOptions: {
            label: 'Teams',
            options: $scope.plTeams,
            valueProp: 'id'
        }
    }, {
        key: 'dwDomains',
        type: 'multiCheckbox',
        templateOptions: {
            label: 'Domains',
            options: $scope.plDomains,
            valueProp: 'id'
        }
    }, {
        key: 'dwTrails',
        type: 'multiCheckbox',
        templateOptions: {
            label: 'Trails',
            options: $scope.plTrails,
            valueProp: 'id'
        }
    }];

    $scope.delete = function(feed) {
        FeedsService.deleteFeed(feed, function() {
            $scope.safeDisplayedfeeds = FeedsService.getFeeds();
            $state.go('^.list');
        });
    };

    $scope.onSubmit = function() {
        FeedsService.upsertFeed($scope.feed, function() {
            $scope.safeDisplayedfeeds = FeedsService.getFeeds();
            $state.go('^.list');
        });
    };

    $scope.loading = true;
    DwFeed.find({filter: {include: ['transmissions','teams','trails','domains','serviceType']}}).$promise
        .then(function (allFeeds) {
            $scope.safeDisplayedfeeds = allFeeds;
            $scope.displayedfeeds = [].concat($scope.safeDisplayedfeeds);
        })
        .catch(function (err) {
            console.log(err);
        })
        .then(function () {
            $scope.loading = false;
        }
    );

    DwServiceType.find({filter: {include: []}}).$promise
        .then(function (allServiceTypes) {
            for (var i = 0; i < allServiceTypes.length; ++i) {
                $scope.plServiceTypes.push({
                    value: allServiceTypes[i].name,
                    name: allServiceTypes[i].name + " - " + allServiceTypes[i].description,
                    id: allServiceTypes[i].id
                });
            }
        })
        .catch(function (err) {
            console.log(err);
        })
        .then(function () {
        }
    );

    DwDomain.find({filter: {include: []}}).$promise
        .then(function (allDomains) {
            for (var i = 0; i < allDomains.length; ++i) {
                $scope.plDomains.push({
                    value: allDomains[i].name,
                    name: allDomains[i].description,
                    id: allDomains[i].id
                });
            }
        })
        .catch(function (err) {
            console.log(err);
        })
        .then(function () {
        }
    );

    DwTrail.find({filter: {include: []}}).$promise
        .then(function (allTrails) {
            for (var i = 0; i < allTrails.length; ++i) {
                $scope.plTrails.push({
                    value: allTrails[i].name,
                    name: allTrails[i].description,
                    id: allTrails[i].id
                });
            }
        })
        .catch(function (err) {
            console.log(err);
        })
        .then(function () {
        }
    );

    DwTeam.find({filter: {include: []}}).$promise
        .then(function (allTeams) {
            for (var i = 0; i < allTeams.length; ++i) {
                $scope.plTeams.push({
                    value: allTeams[i].name,
                    name: allTeams[i].description,
                    id: allTeams[i].id
                });
            }
        })
        .catch(function (err) {
            console.log(err);
        })
        .then(function () {
        }
    );

    if ($stateParams.id) {
        FeedsService.getFeed($stateParams.id).$promise.then(function(result){
            $scope.feed = result;})
    } else {
        $scope.feed = {};
    }
});

<commit_msg>complete refactor of Feeds to use AppAuth.getCurrentUser<commit_after>'use strict';
var app = angular.module('com.module.dwFeeds');

app.controller('FeedsCtrl', function($scope, $state, $stateParams, DwDomain, DwTrail, DwTeam, DwServiceType, FeedsService, gettextCatalog, AppAuth) {

    $scope.feed = {};
    $scope.plProtocols = [
        {"name": "http","description": "http://"},
        {"name": "https","description": "https://" }
    ];
    $scope.plServiceTypes = [];
    $scope.plDomains = [];
    $scope.plTrails = [];
    $scope.plTeams = [];

    $scope.formFields = [{
        key: 'id',
        type: 'input',
        templateOptions: {
            label: gettextCatalog.getString('id'),
            disabled: true
        }
    },{
        key: 'name',
        type: 'input',
        templateOptions: {
            label: gettextCatalog.getString('Name'),
            required: true
        }
    }, {
        key: 'dwServiceTypeId',
        type: 'select',
        templateOptions: {
            label: gettextCatalog.getString('ServiceType'),
            options: $scope.plServiceTypes,
            valueProp: 'id',
            labelProp: 'name',
            required: true,
            disabled: false
        }
    }, {
        key: 'protocol',
        type: 'select',
        templateOptions: {
            label: gettextCatalog.getString('Protocol'),
            options: $scope.plProtocols,
            valueProp: 'name',
            labelProp: 'name',
            required: true,
            disabled: false
        }
    }, {
        key: 'feedUrl',
        type: 'input',
        templateOptions: {
            label: gettextCatalog.getString('Url'),
            required: true
        }
    }, {
        key: 'port',
        type: 'input',
        templateOptions: {
            label: gettextCatalog.getString('Port'),
            required: false
        }
    }, {
        key: 'index',
        type: 'input',
        templateOptions: {
            label: gettextCatalog.getString('Index'),
            required: false
        }
    }, {
        key: 'credentials',
        type: 'input',
        templateOptions: {
            label: gettextCatalog.getString('Credentials'),
            required: false
        }
    }, {
        key: 'dwTeams',
        type: 'multiCheckbox',
        templateOptions: {
            label: 'Teams',
            options: $scope.plTeams,
            valueProp: 'id'
        }
    }, {
        key: 'dwDomains',
        type: 'multiCheckbox',
        templateOptions: {
            label: 'Domains',
            options: $scope.plDomains,
            valueProp: 'id'
        }
    }, {
        key: 'dwTrails',
        type: 'multiCheckbox',
        templateOptions: {
            label: 'Trails',
            options: $scope.plTrails,
            valueProp: 'id'
        }
    }];

    $scope.delete = function(feed) {
        FeedsService.deleteFeed(feed, function() {
            $scope.safeDisplayedfeeds = FeedsService.getFeeds();
            $state.go('^.list');
        });
    };

    $scope.onSubmit = function() {
        FeedsService.upsertFeed($scope.feed, function() {
            $scope.safeDisplayedfeeds = FeedsService.getFeeds();
            $state.go('^.list');
        });
    };

    $scope.loadPicklists = function () {
        DwServiceType.find({filter: {include: []}}).$promise
            .then(function (allServiceTypes) {
                for (var i = 0; i < allServiceTypes.length; ++i) {
                    $scope.plServiceTypes.push({
                        value: allServiceTypes[i].name,
                        name: allServiceTypes[i].name + " - " + allServiceTypes[i].description,
                        id: allServiceTypes[i].id
                    });
                }
            })
            .catch(function (err) {
                console.log(err);
            })
            .then(function () {
            }
        );

        DwDomain.find({filter: {include: []}}).$promise
            .then(function (allDomains) {
                for (var i = 0; i < allDomains.length; ++i) {
                    $scope.plDomains.push({
                        value: allDomains[i].name,
                        name: allDomains[i].description,
                        id: allDomains[i].id
                    });
                }
            })
            .catch(function (err) {
                console.log(err);
            })
            .then(function () {
            }
        );

        DwTrail.find({filter: {include: []}}).$promise
            .then(function (allTrails) {
                for (var i = 0; i < allTrails.length; ++i) {
                    $scope.plTrails.push({
                        value: allTrails[i].name,
                        name: allTrails[i].description,
                        id: allTrails[i].id
                    });
                }
            })
            .catch(function (err) {
                console.log(err);
            })
            .then(function () {
            }
        );

        DwTeam.find({filter: {include: []}}).$promise
            .then(function (allTeams) {
                for (var i = 0; i < allTeams.length; ++i) {
                    $scope.plTeams.push({
                        value: allTeams[i].name,
                        name: allTeams[i].description,
                        id: allTeams[i].id
                    });
                }
            })
            .catch(function (err) {
                console.log(err);
            })
            .then(function () {
            }
        );
    };

    $scope.loading = true;
    AppAuth.getCurrentUser().then(function (currUser) {
        $scope.currentUser = currUser;
        $scope.loadPicklists();
        if ($stateParams.id) {
            FeedsService.getFeed($stateParams.id).$promise.then(function(result){
                $scope.feed = result;
                $scope.safeDisplayedfeeds = {};
                $scope.displayedfeeds = {};
                $scope.loading = false;
            })
        } else {
            FeedsService.getFeeds().$promise.then(function(result){
                $scope.feed = {};
                $scope.safeDisplayedfeeds = result;
                $scope.displayedfeeds = [].concat($scope.safeDisplayedfeeds);
                $scope.loading = false;
            })
        }
    }, function (err) {
        console.log(err);
    });
});
<|endoftext|>


<commit_before>'use strict';

angular.module('frapontillo.bootstrap-switch')
  .directive('bsSwitch', function ($parse, $timeout) {
    return {
      restrict: 'A',
      require: 'ngModel',
      link: function link(scope, element, attrs, controller) {
        var isInit = false;

        /**
         * Return the true value for this specific checkbox.
         * @returns {Object} representing the true view value; if undefined, returns true.
         */
        var getTrueValue = function() {
          if (attrs.type === 'radio') {
            return attrs.value || $parse(attrs.ngValue)(scope) || true;
          }
          var trueValue = ($parse(attrs.ngTrueValue)(scope));
          if (angular.isUndefined(trueValue)) {
            trueValue = true;
          }
          return trueValue;
        };

        /**
         * Get a boolean value from a boolean-like string, evaluating it on the current scope.
         * @param value The input object
         * @returns {boolean} A boolean value
         */
        var getBooleanFromString = function(value) {
          return scope.$eval(value) === true;
        };

        /**
         * Get a boolean value from a boolean-like string, defaulting to true if undefined.
         * @param value The input object
         * @returns {boolean} A boolean value
         */
        var getBooleanFromStringDefTrue = function(value) {
          return (value === true || value === 'true' || !value);
        };

        /**
         * Returns the value if it is truthy, or undefined.
         *
         * @param value The value to check.
         * @returns the original value if it is truthy, {@link undefined} otherwise.
         */
        var getValueOrUndefined = function (value) {
          return (value ? value : undefined);
        };

        /**
         * Returns a function that executes the provided expression
         *
         * @param value The string expression
         * @return a function that evaluates the expression
         */
        var getExprFromString = function (value) {
          if (angular.isUndefined(value)) {
            return angular.noop;
          }
          var fn = $parse(value);
          return angular.bind(null, fn, scope);
        };

        /**
         * Get the value of the angular-bound attribute, given its name.
         * The returned value may or may not equal the attribute value, as it may be transformed by a function.
         *
         * @param attrName  The angular-bound attribute name to get the value for
         * @returns {*}     The attribute value
         */
        var getSwitchAttrValue = function(attrName) {
          var map = {
            'switchRadioOff': getBooleanFromStringDefTrue,
            'switchActive': function(value) {
              return !getBooleanFromStringDefTrue(value);
            },
            'switchAnimate': getBooleanFromStringDefTrue,
            'switchLabel': function(value) {
              return value ? value : ' ';
            },
            'switchIcon': function(value) {
              if (value) {
                return '';
              }
            },
            'switchWrapper': function(value) {
              return value || 'wrapper';
            },
            'switchInverse': getBooleanFromString,
            'switchReadonly': getBooleanFromString,
            'switchChange': getExprFromString
          };
          var transFn = map[attrName] || getValueOrUndefined;
          return transFn(attrs[attrName]);
        };

        /**
         * Set a bootstrapSwitch parameter according to the angular-bound attribute.
         * The parameter will be changed only if the switch has already been initialized
         * (to avoid creating it before the model is ready).
         *
         * @param element   The switch to apply the parameter modification to
         * @param attr      The name of the switch parameter
         * @param modelAttr The name of the angular-bound parameter
         */
        var setSwitchParamMaybe = function(element, attr, modelAttr) {
          if (!isInit) {
            return;
          }
          var newValue = getSwitchAttrValue(modelAttr);
          element.bootstrapSwitch(attr, newValue);
        };

        var setActive = function() {
          setSwitchParamMaybe(element, 'disabled', 'switchActive');
        };

        /**
         * If the directive has not been initialized yet, do so.
         */
        var initMaybe = function() {
          // if it's the first initialization
          if (!isInit) {
            var viewValue = (controller.$modelValue === getTrueValue());
            isInit = !isInit;
            // Bootstrap the switch plugin
            element.bootstrapSwitch({
              radioAllOff: getSwitchAttrValue('switchRadioOff'),
              disabled: getSwitchAttrValue('switchActive'),
              state: viewValue,
              onText: getSwitchAttrValue('switchOnText'),
              offText: getSwitchAttrValue('switchOffText'),
              onColor: getSwitchAttrValue('switchOnColor'),
              offColor: getSwitchAttrValue('switchOffColor'),
              animate: getSwitchAttrValue('switchAnimate'),
              size: getSwitchAttrValue('switchSize'),
              labelText: attrs.switchLabel ? getSwitchAttrValue('switchLabel') : getSwitchAttrValue('switchIcon'),
              wrapperClass: getSwitchAttrValue('switchWrapper'),
              handleWidth: getSwitchAttrValue('switchHandleWidth'),
              labelWidth: getSwitchAttrValue('switchLabelWidth'),
              inverse: getSwitchAttrValue('switchInverse'),
              readonly: getSwitchAttrValue('switchReadonly')
            });
            if (attrs.type === 'radio') {
              controller.$setViewValue(controller.$modelValue);
            } else {
              controller.$setViewValue(viewValue);
            }
          }
        };

        /**
         * Listen to model changes.
         */
        var listenToModel = function () {

          attrs.$observe('switchActive', function (newValue) {
            var active = getBooleanFromStringDefTrue(newValue);
            // if we are disabling the switch, delay the deactivation so that the toggle can be switched
            if (!active) {
              $timeout(function() {
                setActive(active);
              });
            } else {
              // if we are enabling the switch, set active right away
              setActive(active);
            }
          });

          function modelValue() {
            return controller.$modelValue;
          }

          // When the model changes
          scope.$watch(modelValue, function(newValue) {
            initMaybe();
            if (newValue !== undefined) {
              element.bootstrapSwitch('state', newValue === getTrueValue(), false);
            } else {
              element.bootstrapSwitch('toggleIndeterminate', true, false);
            }
          }, true);

          // angular attribute to switch property bindings
          var bindings = {
            'switchRadioOff': 'radioAllOff',
            'switchOnText': 'onText',
            'switchOffText': 'offText',
            'switchOnColor': 'onColor',
            'switchOffColor': 'offColor',
            'switchAnimate': 'animate',
            'switchSize': 'size',
            'switchLabel': 'labelText',
            'switchIcon': 'labelText',
            'switchWrapper': 'wrapperClass',
            'switchHandleWidth': 'handleWidth',
            'switchLabelWidth': 'labelWidth',
            'switchInverse': 'inverse',
            'switchReadonly': 'readonly'
          };

          var observeProp = function(prop, bindings) {
            return function() {
              attrs.$observe(prop, function () {
                setSwitchParamMaybe(element, bindings[prop], prop);
              });
            };
          };

          // for every angular-bound attribute, observe it and trigger the appropriate switch function
          for (var prop in bindings) {
            attrs.$observe(prop, observeProp(prop, bindings));
          }
        };

        /**
         * Listen to view changes.
         */
        var listenToView = function () {

          var switchChangeFn = getSwitchAttrValue('switchChange');

          if (attrs.type === 'radio') {
            // when the switch is clicked
            element.on('change.bootstrapSwitch', function (e) {
              // discard not real change events
              if ((controller.$modelValue === controller.$viewValue) && (e.target.checked !== $(e.target).bootstrapSwitch('state'))) {
                // $setViewValue --> $viewValue --> $parsers --> $modelValue
                // if the switch is indeed selected
                if (e.target.checked) {
                  // set its value into the view
                  controller.$setViewValue(getTrueValue());
                } else if (getTrueValue() === controller.$viewValue) {
                  // otherwise if it's been deselected, delete the view value
                  controller.$setViewValue(undefined);
                }
                switchChangeFn();
              }
            });
          } else {
            // When the checkbox switch is clicked, set its value into the ngModel
            element.on('switchChange.bootstrapSwitch', function (e) {
              // $setViewValue --> $viewValue --> $parsers --> $modelValue
              controller.$setViewValue(e.target.checked);
              switchChangeFn();
            });
          }
        };

        // Listen and respond to view changes
        listenToView();

        // Listen and respond to model changes
        listenToModel();

        // On destroy, collect ya garbage
        scope.$on('$destroy', function () {
          element.bootstrapSwitch('destroy');
        });
      }
    };
  })
  .directive('bsSwitch', function () {
    return {
      restrict: 'E',
      require: 'ngModel',
      template: '<input bs-switch>',
      replace: true
    };
  });
<commit_msg>fix(switchChange): use $evalAsync to force digest cycle<commit_after>'use strict';

angular.module('frapontillo.bootstrap-switch')
  .directive('bsSwitch', function ($parse, $timeout) {
    return {
      restrict: 'A',
      require: 'ngModel',
      link: function link(scope, element, attrs, controller) {
        var isInit = false;

        /**
         * Return the true value for this specific checkbox.
         * @returns {Object} representing the true view value; if undefined, returns true.
         */
        var getTrueValue = function() {
          if (attrs.type === 'radio') {
            return attrs.value || $parse(attrs.ngValue)(scope) || true;
          }
          var trueValue = ($parse(attrs.ngTrueValue)(scope));
          if (angular.isUndefined(trueValue)) {
            trueValue = true;
          }
          return trueValue;
        };

        /**
         * Get a boolean value from a boolean-like string, evaluating it on the current scope.
         * @param value The input object
         * @returns {boolean} A boolean value
         */
        var getBooleanFromString = function(value) {
          return scope.$eval(value) === true;
        };

        /**
         * Get a boolean value from a boolean-like string, defaulting to true if undefined.
         * @param value The input object
         * @returns {boolean} A boolean value
         */
        var getBooleanFromStringDefTrue = function(value) {
          return (value === true || value === 'true' || !value);
        };

        /**
         * Returns the value if it is truthy, or undefined.
         *
         * @param value The value to check.
         * @returns the original value if it is truthy, {@link undefined} otherwise.
         */
        var getValueOrUndefined = function (value) {
          return (value ? value : undefined);
        };

        /**
         * Returns a function that executes the provided expression
         *
         * @param value The string expression
         * @return a function that evaluates the expression
         */
        var getExprFromString = function (value) {
          if (angular.isUndefined(value)) {
            return angular.noop;
          }
          return function () {
            scope.$evalAsync(value);
          };
        };

        /**
         * Get the value of the angular-bound attribute, given its name.
         * The returned value may or may not equal the attribute value, as it may be transformed by a function.
         *
         * @param attrName  The angular-bound attribute name to get the value for
         * @returns {*}     The attribute value
         */
        var getSwitchAttrValue = function(attrName) {
          var map = {
            'switchRadioOff': getBooleanFromStringDefTrue,
            'switchActive': function(value) {
              return !getBooleanFromStringDefTrue(value);
            },
            'switchAnimate': getBooleanFromStringDefTrue,
            'switchLabel': function(value) {
              return value ? value : ' ';
            },
            'switchIcon': function(value) {
              if (value) {
                return '';
              }
            },
            'switchWrapper': function(value) {
              return value || 'wrapper';
            },
            'switchInverse': getBooleanFromString,
            'switchReadonly': getBooleanFromString,
            'switchChange': getExprFromString
          };
          var transFn = map[attrName] || getValueOrUndefined;
          return transFn(attrs[attrName]);
        };

        /**
         * Set a bootstrapSwitch parameter according to the angular-bound attribute.
         * The parameter will be changed only if the switch has already been initialized
         * (to avoid creating it before the model is ready).
         *
         * @param element   The switch to apply the parameter modification to
         * @param attr      The name of the switch parameter
         * @param modelAttr The name of the angular-bound parameter
         */
        var setSwitchParamMaybe = function(element, attr, modelAttr) {
          if (!isInit) {
            return;
          }
          var newValue = getSwitchAttrValue(modelAttr);
          element.bootstrapSwitch(attr, newValue);
        };

        var setActive = function() {
          setSwitchParamMaybe(element, 'disabled', 'switchActive');
        };

        /**
         * If the directive has not been initialized yet, do so.
         */
        var initMaybe = function() {
          // if it's the first initialization
          if (!isInit) {
            var viewValue = (controller.$modelValue === getTrueValue());
            isInit = !isInit;
            // Bootstrap the switch plugin
            element.bootstrapSwitch({
              radioAllOff: getSwitchAttrValue('switchRadioOff'),
              disabled: getSwitchAttrValue('switchActive'),
              state: viewValue,
              onText: getSwitchAttrValue('switchOnText'),
              offText: getSwitchAttrValue('switchOffText'),
              onColor: getSwitchAttrValue('switchOnColor'),
              offColor: getSwitchAttrValue('switchOffColor'),
              animate: getSwitchAttrValue('switchAnimate'),
              size: getSwitchAttrValue('switchSize'),
              labelText: attrs.switchLabel ? getSwitchAttrValue('switchLabel') : getSwitchAttrValue('switchIcon'),
              wrapperClass: getSwitchAttrValue('switchWrapper'),
              handleWidth: getSwitchAttrValue('switchHandleWidth'),
              labelWidth: getSwitchAttrValue('switchLabelWidth'),
              inverse: getSwitchAttrValue('switchInverse'),
              readonly: getSwitchAttrValue('switchReadonly')
            });
            if (attrs.type === 'radio') {
              controller.$setViewValue(controller.$modelValue);
            } else {
              controller.$setViewValue(viewValue);
            }
          }
        };

        /**
         * Listen to model changes.
         */
        var listenToModel = function () {

          attrs.$observe('switchActive', function (newValue) {
            var active = getBooleanFromStringDefTrue(newValue);
            // if we are disabling the switch, delay the deactivation so that the toggle can be switched
            if (!active) {
              $timeout(function() {
                setActive(active);
              });
            } else {
              // if we are enabling the switch, set active right away
              setActive(active);
            }
          });

          function modelValue() {
            return controller.$modelValue;
          }

          // When the model changes
          scope.$watch(modelValue, function(newValue) {
            initMaybe();
            if (newValue !== undefined) {
              element.bootstrapSwitch('state', newValue === getTrueValue(), false);
            } else {
              element.bootstrapSwitch('toggleIndeterminate', true, false);
            }
          }, true);

          // angular attribute to switch property bindings
          var bindings = {
            'switchRadioOff': 'radioAllOff',
            'switchOnText': 'onText',
            'switchOffText': 'offText',
            'switchOnColor': 'onColor',
            'switchOffColor': 'offColor',
            'switchAnimate': 'animate',
            'switchSize': 'size',
            'switchLabel': 'labelText',
            'switchIcon': 'labelText',
            'switchWrapper': 'wrapperClass',
            'switchHandleWidth': 'handleWidth',
            'switchLabelWidth': 'labelWidth',
            'switchInverse': 'inverse',
            'switchReadonly': 'readonly'
          };

          var observeProp = function(prop, bindings) {
            return function() {
              attrs.$observe(prop, function () {
                setSwitchParamMaybe(element, bindings[prop], prop);
              });
            };
          };

          // for every angular-bound attribute, observe it and trigger the appropriate switch function
          for (var prop in bindings) {
            attrs.$observe(prop, observeProp(prop, bindings));
          }
        };

        /**
         * Listen to view changes.
         */
        var listenToView = function () {

          var switchChangeFn = getSwitchAttrValue('switchChange');

          if (attrs.type === 'radio') {
            // when the switch is clicked
            element.on('change.bootstrapSwitch', function (e) {
              // discard not real change events
              if ((controller.$modelValue === controller.$viewValue) && (e.target.checked !== $(e.target).bootstrapSwitch('state'))) {
                // $setViewValue --> $viewValue --> $parsers --> $modelValue
                // if the switch is indeed selected
                if (e.target.checked) {
                  // set its value into the view
                  controller.$setViewValue(getTrueValue());
                } else if (getTrueValue() === controller.$viewValue) {
                  // otherwise if it's been deselected, delete the view value
                  controller.$setViewValue(undefined);
                }
                switchChangeFn();
              }
            });
          } else {
            // When the checkbox switch is clicked, set its value into the ngModel
            element.on('switchChange.bootstrapSwitch', function (e) {
              // $setViewValue --> $viewValue --> $parsers --> $modelValue
              controller.$setViewValue(e.target.checked);
              switchChangeFn();
            });
          }
        };

        // Listen and respond to view changes
        listenToView();

        // Listen and respond to model changes
        listenToModel();

        // On destroy, collect ya garbage
        scope.$on('$destroy', function () {
          element.bootstrapSwitch('destroy');
        });
      }
    };
  })
  .directive('bsSwitch', function () {
    return {
      restrict: 'E',
      require: 'ngModel',
      template: '<input bs-switch>',
      replace: true
    };
  });
<|endoftext|>


<commit_before>ngGridDirectives.directive('ngInput', [function() {
    return {
        require: 'ngModel',
        link: function (scope, elm, attrs, ngModel) {
            // Store the initial cell value so we can reset to it if need be
            var oldCellValue;
            var dereg = scope.$watch('ngModel', function() {
                oldCellValue = ngModel.$modelValue;
                dereg(); // only run this watch once, we don't want to overwrite our stored value when the input changes
            });

            elm.bind('keydown', function(evt) {
                switch (evt.keyCode) {
                    case 37: // Left arrow
                    case 38: // Up arrow
                    case 39: // Right arrow
                    case 40: // Down arrow
                        evt.stopPropagation();
                        break;
                    case 27: // Esc (reset to old value)
                        if (!scope.$$phase) {
                            scope.$apply(function() {
                                ngModel.$setViewValue(oldCellValue);
                                elm.blur();
                            });
                        }
                        break;
                    case 13: // Enter (Leave Field)
                        if(scope.enableCellEditOnFocus && scope.totalFilteredItemsLength() - 1 > scope.row.rowIndex && scope.row.rowIndex > 0  || scope.enableCellEdit) {
                            elm.blur();
                        }
                        break;
                }

                return true;
            });

            elm.bind('click', function(evt) {
                evt.stopPropagation();
            });

            elm.bind('mousedown', function(evt) {
                evt.stopPropagation();
            });

            scope.$on('ngGridEventStartCellEdit', function () {
                elm.focus();
                elm.select();
            });

            angular.element(elm).bind('blur', function () {
                scope.$emit('ngGridEventEndCellEdit');
            });
        }
    };
}]);<commit_msg>Update ng-input.js<commit_after>ngGridDirectives.directive('ngInput', [function() {
    return {
        require: 'ngModel',
        link: function (scope, elm, attrs, ngModel) {
            // Store the initial cell value so we can reset to it if need be
            var oldCellValue;
            var dereg = scope.$watch('ngModel', function() {
                oldCellValue = ngModel.$modelValue;
                dereg(); // only run this watch once, we don't want to overwrite our stored value when the input changes
            });

            elm.bind('keydown', function(evt) {
                switch (evt.keyCode) {
                    case 37: // Left arrow
                    case 38: // Up arrow
                    case 39: // Right arrow
                    case 40: // Down arrow
                        evt.stopPropagation();
                        break;
                    case 27: // Esc (reset to old value)
                        if (!scope.$$phase) {
                            scope.$apply(function() {
                                ngModel.$setViewValue(oldCellValue);
                                elm.blur();
                            });
                        }
                        break;
                    case 13: // Enter (Leave Field)
                        if(scope.enableCellEditOnFocus && scope.totalFilteredItemsLength() - 1 > scope.row.rowIndex && scope.row.rowIndex > 0  || scope.col.enableCellEdit) {
                            elm.blur();
                        }
                        break;
                }

                return true;
            });

            elm.bind('click', function(evt) {
                evt.stopPropagation();
            });

            elm.bind('mousedown', function(evt) {
                evt.stopPropagation();
            });

            scope.$on('ngGridEventStartCellEdit', function () {
                elm.focus();
                elm.select();
            });

            angular.element(elm).bind('blur', function () {
                scope.$emit('ngGridEventEndCellEdit');
            });
        }
    };
}]);
<|endoftext|>