<commit_before>const postcss = require("postcss");

const {parse}             = require("./parse");
const {indentMultiline}   = require("./utils");
const {getAlignContent}   = require("./align-content");
const {getJustifyContent} = require("./justify-content");
const {getAlignSelf}      = require("./align-self");
const {getJustifySelf}    = require("./justify-self");
const {getGridRows}       = require("./grid-template-rows");
const {getGridCols}       = require("./grid-template-columns");
const {getGridAreas}      = require("./grid-template-areas");
const {getFallback}       = require("./fallback");

module.exports = function (options = {}) {
	return function (css, result) {
		css.walkDecls(function (decl) {
			if (decl.prop === 'grid-kiss') {

				const input  = parse(decl);
				const grid   = { props: new Map, rule: decl.parent };
				const zones  = [];
				const indent = decl.raws.before.match(/.*$/)[0];

				grid.props.set("display", "grid");
				grid.props.set("align-content", getAlignContent(input));
				grid.props.set("justify-content", getJustifyContent(input));
				grid.props.set("grid-template-rows", getGridRows(input));
				grid.props.set("grid-template-columns", getGridCols(input));
				grid.props.set("grid-template-areas", indentMultiline(getGridAreas(input), indent));

				// grid properties
				for (let [prop,value] of grid.props) {
					if (value != null){
						decl.cloneAfter({ prop, value });
					}
				}

				// zone declarations
				for(let zone of input.zones.filter(zone => zone.selector != null)){
					let props = new Map;

					props.set("grid-area", zone.name);
					props.set("justify-self", getJustifySelf(zone));
					props.set("align-self", getAlignSelf(zone));

					let rule = postcss.rule({
						selector: `${grid.rule.selector} > ${zone.selector}`,
						source: decl.source
					});

					for (let [prop,value] of props) {
						if (value != null){
							rule.append({prop, value});
						}
					}

					let lastRule = zones.length > 0 ? zones[zones.length-1].rule : grid.rule;
					grid.rule.parent.insertAfter(lastRule, rule);
					zones.push({ props, rule, zone })
				}

				if(options.fallback){
					const atRule = postcss.atRule({
						name: "supports",
						params: 'not (grid-template-areas:"test")'
					});

					const fallback = getFallback({
						zones, grid, input, decl, result
					});

					atRule.append(fallback.grid.rule);
					for(let [zone, zoneFallback] of fallback.zones){
						atRule.append(zoneFallback.rule);
					}

					let lastRule = zones.length > 0 ? zones[zones.length-1].rule : grid.rule;
					grid.rule.parent.insertAfter(lastRule, atRule);
				}

				decl.remove();
			}
		})
	}
};<commit_msg>fix order of grid container properties<commit_after>const postcss = require("postcss");

const {parse}             = require("./parse");
const {indentMultiline}   = require("./utils");
const {getAlignContent}   = require("./align-content");
const {getJustifyContent} = require("./justify-content");
const {getAlignSelf}      = require("./align-self");
const {getJustifySelf}    = require("./justify-self");
const {getGridRows}       = require("./grid-template-rows");
const {getGridCols}       = require("./grid-template-columns");
const {getGridAreas}      = require("./grid-template-areas");
const {getFallback}       = require("./fallback");

module.exports = function (options = {}) {
	return function (css, result) {
		css.walkDecls(function (decl) {
			if (decl.prop === 'grid-kiss') {

				const input  = parse(decl);
				const grid   = { props: new Map, rule: decl.parent };
				const zones  = [];
				const indent = decl.raws.before.match(/.*$/)[0];

				grid.props.set("display", "grid");
				grid.props.set("align-content", getAlignContent(input));
				grid.props.set("justify-content", getJustifyContent(input));
				grid.props.set("grid-template-rows", getGridRows(input));
				grid.props.set("grid-template-columns", getGridCols(input));
				grid.props.set("grid-template-areas", indentMultiline(getGridAreas(input), indent));

				// grid properties
				for (let [prop,value] of grid.props) {
					if (value != null){
						decl.cloneBefore({ prop, value });
					}
				}

				// zone declarations
				for(let zone of input.zones.filter(zone => zone.selector != null)){
					let props = new Map;

					props.set("grid-area", zone.name);
					props.set("justify-self", getJustifySelf(zone));
					props.set("align-self", getAlignSelf(zone));

					let rule = postcss.rule({
						selector: `${grid.rule.selector} > ${zone.selector}`,
						source: decl.source
					});

					for (let [prop,value] of props) {
						if (value != null){
							rule.append({prop, value});
						}
					}

					let lastRule = zones.length > 0 ? zones[zones.length-1].rule : grid.rule;
					grid.rule.parent.insertAfter(lastRule, rule);
					zones.push({ props, rule, zone })
				}

				if(options.fallback){
					const atRule = postcss.atRule({
						name: "supports",
						params: 'not (grid-template-areas:"test")'
					});

					const fallback = getFallback({
						zones, grid, input, decl, result
					});

					atRule.append(fallback.grid.rule);
					for(let [zone, zoneFallback] of fallback.zones){
						atRule.append(zoneFallback.rule);
					}

					let lastRule = zones.length > 0 ? zones[zones.length-1].rule : grid.rule;
					grid.rule.parent.insertAfter(lastRule, atRule);
				}

				decl.remove();
			}
		})
	}
};<|endoftext|>


<commit_before>// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import BootstrapVue from 'bootstrap-vue'
import VeeValidate from 'vee-validate'
import App from './App'
import store from './store'
import router from './router'
import { sync } from 'vuex-router-sync'
import VuesticPlugin from 'src/components/vuestic-components/vuestic-components-plugin'

Vue.use(VuesticPlugin)
Vue.use(BootstrapVue)
Vue.use(VeeValidate)

sync(store, router)

let mediaHandler = () => {
  if (window.matchMedia(store.getters.config.windowMatchSizeLg).matches) {
    store.dispatch('toggleSidebar', true)
  } else {
    store.dispatch('toggleSidebar', false)
  }
}

router.beforeEach((to, from, next) => {
  store.commit('setLoading', true)
  next()
})

router.afterEach((to, from) => {
  mediaHandler()
  store.commit('setLoading', false)
})

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  store,
  template: '<App/>',
  components: { App }
})
<commit_msg>Update main.js<commit_after>// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import BootstrapVue from 'bootstrap-vue'
import VeeValidate from 'vee-validate'
import App from './App'
import store from './store'
import router from './router'
import { sync } from 'vuex-router-sync'
import VuesticPlugin from 'src/components/vuestic-components/vuestic-components-plugin'

Vue.use(VuesticPlugin)
Vue.use(BootstrapVue)

// NOTE: workaround for VeeValidate + vuetable-2
Vue.use(VeeValidate, {fieldsBagName: 'formFields'})

sync(store, router)

let mediaHandler = () => {
  if (window.matchMedia(store.getters.config.windowMatchSizeLg).matches) {
    store.dispatch('toggleSidebar', true)
  } else {
    store.dispatch('toggleSidebar', false)
  }
}

router.beforeEach((to, from, next) => {
  store.commit('setLoading', true)
  next()
})

router.afterEach((to, from) => {
  mediaHandler()
  store.commit('setLoading', false)
})

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  store,
  template: '<App/>',
  components: { App }
})
<|endoftext|>


<commit_before>import Plugin from 'extplug/Plugin'
import ChatView from 'plug/views/rooms/chat/ChatView'
import PopoutChatView from 'plug/views/rooms/popout/PopoutChatView'
import Events from 'plug/core/Events'
import { around } from 'meld'
import style from './style.css'

const ShowDeleted = Plugin.extend({
  name: 'Show Deleted Messages',
  description: 'Keeps showing deleted chat messages.',

  style,

  enable() {
    function advice(joinpoint) {
      const [ cid ] = joinpoint.args
      // if the last received message is being deleted, make sure new
      // messages don't collapse into its

      if (this.lastText && this.lastText.hasClass(`cid-${cid}`)) {
        this.lastID = this.lastType = this.lastText = this.lastTime = null
      }

      const message = this.$(`.cid-${cid}`).closest('.cm')
      if (!message.hasClass('extplug-deleted')) {
        message.addClass('extplug-deleted')
        message.find('.timestamp').prepend('[Deleted] ')
      }
    }

    this.replaceEvents(() => {
      this.mainAdvice = around(ChatView.prototype, 'onDelete', advice)
      this.popAdvice = around(PopoutChatView.prototype, 'onDelete', advice)
    })
  },

  disable() {
    this.replaceEvents(() => {
      this.mainAdvice.remove()
      this.popAdvice.remove()
    })
    this._super()
  },

  // safely replace the onDelete method, keeping the event handler around
  replaceEvents(cb) {
    const chatView = this.ext.appView.room.chat
    if (chatView) Events.off('chat:delete', chatView.onDelete)
    cb()
    if (chatView) Events.on('chat:delete', chatView.onDelete, chatView)
  }
})

export default ShowDeleted
<commit_msg>Add tooltip showing the moderator who deleted the message.<commit_after>/* global API */
import Plugin from 'extplug/Plugin'
import ChatView from 'plug/views/rooms/chat/ChatView'
import PopoutChatView from 'plug/views/rooms/popout/PopoutChatView'
import Events from 'plug/core/Events'
import { around } from 'meld'
import style from './style.css'

const ShowDeleted = Plugin.extend({
  name: 'Show Deleted Messages',
  description: 'Keeps showing deleted chat messages.',

  style,

  enable () {
    let lastDeleteEvent = null

    function advice (joinpoint) {
      const [ cid ] = joinpoint.args
      // if the last received message is being deleted, make sure new
      // messages don't collapse into its

      if (this.lastText && this.lastText.hasClass(`cid-${cid}`)) {
        this.lastID = this.lastType = this.lastText = this.lastTime = null
      }

      const message = this.$(`.cid-${cid}`).closest('.cm')
      if (!message.hasClass('extplug-deleted')) {
        message.addClass('extplug-deleted')
        const timestamp = message.find('.timestamp')
        timestamp.prepend('[Deleted] ')

        // Add a tooltip showing the user who deleted the message.
        const moderator = lastDeleteEvent && API.getUser(lastDeleteEvent.mi)
        if (moderator) {
          timestamp.attr({
            'data-tooltip-dir': 'left',
            'data-tooltip': `Deleted by ${moderator.username}`
          })
        }
      }

      lastDeleteEvent = null
    }

    this.listenTo('socket:chatDelete', (del) => {
      lastDeleteEvent = del
    })

    this.replaceEvents(() => {
      this.mainAdvice = around(ChatView.prototype, 'onDelete', advice)
      this.popAdvice = around(PopoutChatView.prototype, 'onDelete', advice)
    })
  },

  disable () {
    this.replaceEvents(() => {
      this.mainAdvice.remove()
      this.popAdvice.remove()
    })
    this._super()
  },

  // safely replace the onDelete method, keeping the event handler around
  replaceEvents (cb) {
    const chatView = this.ext.appView.room.chat
    if (chatView) Events.off('chat:delete', chatView.onDelete)
    cb()
    if (chatView) Events.on('chat:delete', chatView.onDelete, chatView)
  }
})

export default ShowDeleted
<|endoftext|>


<commit_before>function RunKonvaStuff(stHeight, stWidth) {
    var stage = new Konva.Stage({
      container:'container',
      height: stHeight,
      width: stWidth
    });
    var integrator = function(f, start, end, step) {
      var total = 0;
      step = step || 0.01;
      for (var i = start; i < end; i+= step) {
        total += f(i + step/2) * step;
      }
      return total;
    };
    var arcFunction = function(x) {
      var a = params.a, b = params.b;
      return Math.sqrt(
        Math.pow(a * Math.sin(x), 2) +
        Math.pow(b * Math.cos(x), 2)
      );
    };
    math.import({
      integrate: integrator
    });
    var params = {};
    params.a = 150;
    params.b = 100;
    params.factor = 6;

    params.getPerimiter = function() {
      return math.integrate(arcFunction, 0, 2* Math.PI);
    };

    params.getCircleRadius = function() {
      var ep = this.getPerimiter();
      var pm = ep/this.factor;
      return pm / (2 * Math.PI);
    };

    params.getArcLength = function(t) {
      return math.integrate(arcFunction, 0, t);
    };

    params.getCirclePerimeter = function() {
      return Math.PI * 2 * this.getCircleRadius();
    };

    var layer = new Konva.Layer();
    var ellipse = new Konva.Ellipse({
      x: stWidth/2,
      y: stHeight/2,
      stroke: 'red',
      radius: {
        x: params.a,
        y: params.b
      },
      strokeWidth: 1
    });

    var points = [0,0 , 150, 0];

    var line = new Konva.Line({
      stroke: 'black',
      strokeWidth: 1,
      points: points,
      x:stWidth/2,
      y: stHeight/2
    });
    var spoints = [];
    var spline = new Konva.Line({
      x: 0,
      y: 0,
      points: spoints,
      stroke: 'green',

      strokeWidth: 2
    });
    var layer2 = new Konva.Layer();
    layer2.add(spline);

    var r = params.getCircleRadius();
    var circle = new Konva.Circle({
      x: ellipse.x() + params.a + r,
      y: ellipse.y(),
      radius: r,
      stroke: 'blue',
      strokeWidth: 1
    });
    var cline = new Konva.Line({
      x: circle.x(),
      y: circle.y(),
      points: [0,0, -circle.radius(), 0],
      stroke: 'green',
      visible: true
    });
    var point = new Konva.Circle({
      x: ellipse.x() + params.a,
      y: ellipse.y(),
      radius: 2,
      fill:'green'
    });
    var group = new Konva.Group({
      y: circle.y(),
      x: circle.x(),
    });
    // group.add(line, circle);
    group.add(cline, point, circle);
    group.offset({x: circle.radius(), y: circle.radius()});
    // group.setPosition(circle.getPosition());
    group.offset(circle.getPosition());

    layer.add(ellipse, line, group);
    stage.add(layer);
    stage.add(layer2);
    var angSpeed = 360 / 4;
    var theta = 0;
    var cp = params.getCirclePerimeter();
    var getRotationDeg = function(arcLength) {
      if(arcLength < cp) {
        return arcLength * 180 / (Math.PI * circle.radius());
      }
      else {
        // arcLength > cp
        var revolutions = Math.floor(arcLength/cp);
        var remaining = arcLength - revolutions * cp;
        return remaining * 180 / (Math.PI * circle.radius());
      }
    };
    var animate = function(frame) {
      if(theta > 360) {
        theta = 0;
        //layer2.clear();
        spoints.length = 0;
        spline.points(spoints);
      }
      theta += frame.timeDiff * angSpeed/1000;
      var thRad = theta * Math.PI/180;
      var rx, ry, rcx, rcy;
      rx = params.a * Math.cos(thRad);
      ry = params.b * Math.sin(thRad);
      rcx = rx + circle.radius() * Math.cos(thRad);
      rcy = ry + circle.radius() * Math.sin(thRad);
      points[points.length - 2] = rx;
      points[points.length - 1] = ry;

      var
        pos = line.getPosition(),
        cx = pos.x + rcx,
        cy = pos.y + rcy;
      // group.setPosition({x: cx, y: cy});
      group.setPosition({x: cx, y: cy});
      var al = params.getArcLength(thRad);
      var deg = getRotationDeg(al);
      group.setRotation(deg);
      var p = point.getAbsolutePosition();
      console.log(spoints.length);
      spoints.push(p.x, p.y);
      spline.points(spoints);
    };

    var anim = new Konva.Animation(animate, [layer, layer2]);
    setTimeout(function(){
      anim.start();
    }, 2000);
}

window.addEventListener('load', function(){
  var container = document.getElementById('container');
  var left = container.parentNode.offsetLeft;
  var top = container.offsetTop;
  var maxHt = window.innerHeight - top - left;
  var maxWd = window.innerWidth - left * 2;
  RunKonvaStuff(maxHt, maxWd);
}, false);
<commit_msg>fixed waviness - increased the precision of integration<commit_after>function RunKonvaStuff(stHeight, stWidth) {
    var stage = new Konva.Stage({
      container:'container',
      height: stHeight,
      width: stWidth
    });
    var integrator = function(f, start, end, step) {
      var total = 0;
      step = step || 0.001;
      for (var i = start; i < end; i+= step) {
        total += f(i + step/2) * step;
      }
      return total;
    };
    var arcFunction = function(x) {
      var a = params.a, b = params.b;
      return Math.sqrt(
        Math.pow(a * Math.sin(x), 2) +
        Math.pow(b * Math.cos(x), 2)
      );
    };
    math.import({
      integrate: integrator
    });
    var params = {};
    params.a = 120;
    params.b = 80;
    params.factor = 4;

    params.getPerimiter = function() {
      return math.integrate(arcFunction, 0, 2* Math.PI);
    };

    params.getCircleRadius = function() {
      var ep = this.getPerimiter();
      var pm = ep/this.factor;
      return pm / (2 * Math.PI);
    };

    params.getArcLength = function(t) {
      return math.integrate(arcFunction, 0, t);
    };

    params.getCirclePerimeter = function() {
      return Math.PI * 2 * this.getCircleRadius();
    };

    var layer = new Konva.Layer();
    var ellipse = new Konva.Ellipse({
      x: stWidth/2,
      y: stHeight/2,
      stroke: 'red',
      radius: {
        x: params.a,
        y: params.b
      },
      strokeWidth: 1
    });

    var points = [0,0 , params.a, 0];

    var line = new Konva.Line({
      stroke: 'black',
      strokeWidth: 1,
      points: points,
      x:stWidth/2,
      y: stHeight/2
    });
    var spoints = [];
    var spline = new Konva.Line({
      x: 0,
      y: 0,
      points: spoints,
      stroke: 'green',

      strokeWidth: 2
    });
    var layer2 = new Konva.Layer();
    layer2.add(spline);

    var r = params.getCircleRadius();
    var circle = new Konva.Circle({
      x: ellipse.x() + params.a + r,
      y: ellipse.y(),
      radius: r,
      stroke: 'blue',
      strokeWidth: 1
    });
    var cline = new Konva.Line({
      x: circle.x(),
      y: circle.y(),
      points: [0,0, -circle.radius(), 0],
      stroke: 'green',
      visible: true
    });
    var point = new Konva.Circle({
      x: ellipse.x() + params.a,
      y: ellipse.y(),
      radius: 2,
      fill:'green'
    });
    var group = new Konva.Group({
      y: circle.y(),
      x: circle.x(),
    });
    // group.add(line, circle);
    group.add(cline, point, circle);
    group.offset({x: circle.radius(), y: circle.radius()});
    // group.setPosition(circle.getPosition());
    group.offset(circle.getPosition());

    layer.add(ellipse, line, group);
    stage.add(layer);
    stage.add(layer2);
    var angSpeed = 360 / 4;
    var theta = 0;
    var cp = params.getCirclePerimeter();
    var getRotationDeg = function(arcLength) {
      if(arcLength < cp) {
        return arcLength * 180 / (Math.PI * circle.radius());
      }
      else {
        // arcLength > cp
        var revolutions = Math.floor(arcLength/cp);
        var remaining = arcLength - revolutions * cp;
        return remaining * 180 / (Math.PI * circle.radius());
      }
    };
    var animate = function(frame) {
      if(theta > 360) {
        theta = 0;
        //layer2.clear();
        spoints.length = 0;
        spline.points(spoints);
      }
      theta += frame.timeDiff * angSpeed/1000;
      var thRad = theta * Math.PI/180;
      var rx, ry, rcx, rcy;
      rx = params.a * Math.cos(thRad);
      ry = params.b * Math.sin(thRad);
      rcx = rx + circle.radius() * Math.cos(thRad);
      rcy = ry + circle.radius() * Math.sin(thRad);
      points[points.length - 2] = rx;
      points[points.length - 1] = ry;

      var
        pos = line.getPosition(),
        cx = pos.x + rcx,
        cy = pos.y + rcy;
      // group.setPosition({x: cx, y: cy});
      group.setPosition({x: cx, y: cy});
      var al = params.getArcLength(thRad);
      var deg = getRotationDeg(al);
      group.setRotation(deg);
      var p = point.getAbsolutePosition();
      console.log(spoints.length);
      spoints.push(p.x, p.y);
      spline.points(spoints);
    };

    var anim = new Konva.Animation(animate, [layer, layer2]);
    setTimeout(function(){
      anim.start();
    }, 2000);
}

window.addEventListener('load', function(){
  var container = document.getElementById('container');
  var left = container.parentNode.offsetLeft;
  var top = container.offsetTop;
  var maxHt = window.innerHeight - top - left;
  var maxWd = window.innerWidth - left * 2;
  RunKonvaStuff(maxHt, maxWd);
}, false);
<|endoftext|>


<commit_before>(function(){
	var bgObj;
	var bgStaticLayer=$().$add('div',{
		'className': 'mod_bg',
		'style': {
			'display': 'none',
			'zIndex': g8v.bgLayer++
		}
	});

	var bg={
		'load': function(data){
			if(/^#([a-fA-F0-9]{3}|[a-fA-F0-9]{6})$/.test(data)){//RGB Color
				bgStaticLayer.style.display='';
				bg.mapUnload();
				bgStaticLayer.style.background=data;
			}else if(/^http(s)?:\/\//.test(data)){//img
				bgStaticLayer.style.display='';
				bg.mapUnload();
				bgStaticLayer.style.background='url('+data+')';
			}else if(/^\w+$/.test(data)){//OSM
				bgStaticLayer.style.display='none';
				bg.mapLoad(data);
			}else return;
			if(!bgObj)
				bgObj=g8v.createObj('bg',[data]);
			else
				bgObj.args[0]=data;
			g8v.updateShareUrl();
		}
	};
	//bgMap
	(function(){
		if(!window.WebSocket){
			alert('你的瀏覽器過舊，不支援背景地圖功能，請更換或更新瀏覽器');
			return;
		}
		var transform=function(x,y){
			return ol.proj.transform((y!=undefined)? [parseFloat(x,10),parseFloat(y,10)]:[parseFloat(x[0],10),parseFloat(x[1],10)],'EPSG:4326','EPSG:900913');
		};
		var socket,code;
		var markList={};
		var markObj=$.tag('div',{
			'className': 'mod_bg_mark'
		}).$add('div',{
			'className': 'name'
		},null,true);
		var waitMark=false;
		var bgMapLayerIndex=g8v.bgLayer++;
		var bgMapLayer=$().$add('div',{
			'className': 'mod_bg',
			'style': {
				'display': 'none',
				'zIndex': bgMapLayerIndex
			}
		});
		var mapView=new ol.View({
			center: transform(0,0),
			zoom: 1
		});
		var map=new ol.Map({
			'controls': ol.control.defaults({
				'attributionOptions': {
					'collapsible': false
				}
			}),
			'interactions': ol.interaction.defaults().extend([new ol.interaction.Select()]),
			'target': bgMapLayer,
			'layers': [new ol.layer.Tile({source: new ol.source.OSM()})],
			'view': mapView
		});
		bg.mapLoad=(function(){
			var tryConnect=0;
			var ev_message=function(e){
				console.log(e.data);
				var msg=JSON.parse(e.data);
				switch(msg.action){
					case 'move':
						bg.mapMark(msg.name,msg.pos,msg.module,msg.args,true);
					break;
					case 'delete':
						bg.mapUnMark(msg.name,true);
					break;
					case 'viewAll':
						var x=[];
						var y=[];
						var name,pos;
						for(name in markList){
							pos=markList[name].pos;
							x.push(pos[0]);
							y.push(pos[1]);
						}
						var min=transform(Math.min.apply(Math,x),Math.min.apply(Math,y));
						var max=transform(Math.max.apply(Math,x),Math.max.apply(Math,y));
						mapView.fitExtent([min[0],min[1],max[0],max[1]],map.getSize());
					break;
					default:
						console.error('[bgMap] 不支援的操作 %s,msg=%s',msg.action,JSON.stringify(msg));
				}
			}
			var ev_open=function(){
				tryConnect=0;
			}
			var ev_close=function(){
				if(!code) return;
				if(tryConnect>=3){
					console.log('[bgMap] 無法與伺服器連線，放棄連線');
					alert('背景地圖無法與伺服器連線，請檢查網路連線，或者目前伺服器發生異常');
					return;
				}
				if(tryConnect){
					console.log('[bgMap] 無法與伺服器連線，嘗試次數：%d',tryConnect);
					setTimeout(bg.mapLoad,3000,code);
				}else{
					console.error('[bgMap] 失去連線，3 秒後重新連線');
					setTimeout(bg.mapLoad,3000,code);
				}
			}
			return function(data){
				if(!/^\w+$/.test(data)) return false;
				bg.mapUnload();
				code=data;
				bgMapLayer.style.display='';
				map.updateSize();
				tryConnect++;
				//socket=new WebSocket('ws://192.168.1.123:10080/'+code,'mapPoint');
				socket=new WebSocket('ws://g8v-a0000778.rhcloud.com:8000/'+code,'mapPoint');
				socket.addEventListener('message',ev_message);
				socket.addEventListener('open',ev_open);
				socket.addEventListener('close',ev_close);
			};
		})();
		bg.mapUnload=function(){
			if(!code) return;
			socket.close();
			socket=undefined;
			var k,o
			for(k in markList){
				o=markList[k];
				map.removeOverlay(o.layer);
				o.obj.$del();
				markList[k]=undefined;
				delete markList[k];
			}
			bgMapLayer.style.display='none';
			code=null;
			bg.mapAbortMark();
		};
		bg.mapMark=function(name,pos,mod,args,formServer){
			var mark=markList['p_'+name];
			if(!mark){
				var obj=markObj.cloneNode(true);
				obj.querySelector('.name').textContent=name;
				obj.addEventListener('click',function(){
					if(!(mark.module.length && g8v.module.hasOwnProperty(mark.module))) return;
					var mod=g8v.module[mark.module];
					mod.load.apply(mod,mark.args);
				});
				mark=markList['p_'+name]={
					'name': name,
					'pos': pos,
					'module': mod || '',
					'args': args || [],
					'layer': new ol.Overlay({
						'position': transform(pos[0],pos[1]),
						'element': obj
					}),
					'obj': obj
				};
				map.addOverlay(mark.layer);
				obj=undefined;
				delete obj;
			}else{
				if(pos && pos.length===2){
					mark.pos=pos;
					mark.layer.setPosition(transform(pos[0],pos[1]))
				}
				if(mod && args){
					mark.module=mod;
					mark.args=args;
				}
			}
			if(!formServer)
				socket.send(JSON.stringify({
					'action': 'move',
					'name': name,
					'pos': mark.pos,
					'module': mark.mod,
					'args': mark.args
				}));
		};
		bg.mapUnMark=function(name,formServer){
			var mark=markList['p_'+name];
			if(!mark) return;
			map.removeOverlay(mark.layer);
			mark=markList['p_'+name]=undefined;
			delete mark,markList['p_'+name];
			if(!formServer)
				socket.send(JSON.stringify({
					'action': 'delete',
					'name': name
				}));
		};
		bg.mapStartMark=function(obj){
			if(!code){
				alert('標記地點需搭配背景地圖使用！');
				return;
			}
			if(waitMark){
				return false;
			}
			bgMapLayer.style.zIndex=2147483646;
			alert('請擊點欲標記的位址');
			waitMark=obj;
			return true;
		};
		bg.mapAbortMark=function(){
			bgMapLayer.style.zIndex=bgMapLayerIndex;
			waitMark=false;
		};
		g8v.windowOption.push(function(obj){
			return $.tag('span',{
				'textContent': '地',
				'addEventListener': ['click',function(e){
					if(!bg.mapStartMark(obj))
						alert('其他項目正在標記中');
				}]
			});
		});
		map.on('click',function(e){
			if(!waitMark) return;
			var name;
			while(!(name=prompt('請輸入標記點名稱：(輸入現存標記點名稱則表示更動該標記點)'))){
				if(name!==null) continue;
				bg.mapAbortMark();
				alert('取消標記');
				return;
			}
			if(markList['p_'+name] && !confirm('確定要更動標記點 '+name+' ?')) return;
			bg.mapMark(
				name,
				ol.proj.transform(e.coordinate,'EPSG:900913','EPSG:4326'),
				waitMark.module,
				(waitMark.title && waitMark.width && waitMark.height && waitMark.posX!=undefined && waitMark.posY!=undefined)?
					waitMark.args.concat([waitMark.title,waitMark.posX,waitMark.posY,waitMark.width,waitMark.height]):
					waitMark.args
			);
			bg.mapAbortMark();
		});
	})();

	var controlForm=$.tag('form',{'textContent':'設定背景：'});
	var inputBgData=controlForm.$add('input',{'type':'input','placeholder':'圖片、RGB、標記地圖'});
	controlForm.$add('input',{'type':'submit','value':'設定'});
	controlForm.addEventListener('submit',function(e){
		e.preventDefault();
		bg.load(inputBgData.value);
	});
	g8v.module.config.addItem(controlForm);

	document.head.$add('link',{
		'type': 'text/css',
		'rel': 'stylesheet',
		'href': (navigator.userAgent.indexOf('Firefox')!==-1)? 'module/bg_firefox.css':'module/bg.css'
	});
	g8v.module.bg=bg;
})();<commit_msg>背景地圖移除標記<commit_after>(function(){
	var bgObj;
	var bgStaticLayer=$().$add('div',{
		'className': 'mod_bg',
		'style': {
			'display': 'none',
			'zIndex': g8v.bgLayer++
		}
	});

	var bg={
		'load': function(data){
			if(/^#([a-fA-F0-9]{3}|[a-fA-F0-9]{6})$/.test(data)){//RGB Color
				bgStaticLayer.style.display='';
				bg.mapUnload();
				bgStaticLayer.style.background=data;
			}else if(/^http(s)?:\/\//.test(data)){//img
				bgStaticLayer.style.display='';
				bg.mapUnload();
				bgStaticLayer.style.background='url('+data+')';
			}else if(/^\w+$/.test(data)){//OSM
				bgStaticLayer.style.display='none';
				bg.mapLoad(data);
			}else return;
			if(!bgObj)
				bgObj=g8v.createObj('bg',[data]);
			else
				bgObj.args[0]=data;
			g8v.updateShareUrl();
		}
	};
	//bgMap
	(function(){
		if(!window.WebSocket){
			alert('你的瀏覽器過舊，不支援背景地圖功能，請更換或更新瀏覽器');
			return;
		}
		var transform=function(x,y){
			return ol.proj.transform((y!=undefined)? [parseFloat(x,10),parseFloat(y,10)]:[parseFloat(x[0],10),parseFloat(x[1],10)],'EPSG:4326','EPSG:900913');
		};
		var socket,code;
		var markList={};
		var markObj=$.tag('div',{
			'className': 'mod_bg_mark'
		}).$add('div',{
			'className': 'name'
		},null,true);
		var waitMark=false;
		var bgMapLayerIndex=g8v.bgLayer++;
		var bgMapLayer=$().$add('div',{
			'className': 'mod_bg',
			'style': {
				'display': 'none',
				'zIndex': bgMapLayerIndex
			}
		});
		var mapView=new ol.View({
			center: transform(0,0),
			zoom: 1
		});
		var map=new ol.Map({
			'controls': ol.control.defaults({
				'attributionOptions': {
					'collapsible': false
				}
			}),
			'interactions': ol.interaction.defaults().extend([new ol.interaction.Select()]),
			'target': bgMapLayer,
			'layers': [new ol.layer.Tile({source: new ol.source.OSM()})],
			'view': mapView
		});
		bg.mapLoad=(function(){
			var tryConnect=0;
			var ev_message=function(e){
				console.log(e.data);
				var msg=JSON.parse(e.data);
				switch(msg.action){
					case 'move':
						bg.mapMark(msg.name,msg.pos,msg.module,msg.args,true);
					break;
					case 'delete':
						bg.mapUnMark(msg.name,true);
					break;
					case 'viewAll':
						var x=[];
						var y=[];
						var name,pos;
						for(name in markList){
							pos=markList[name].pos;
							x.push(pos[0]);
							y.push(pos[1]);
						}
						var min=transform(Math.min.apply(Math,x),Math.min.apply(Math,y));
						var max=transform(Math.max.apply(Math,x),Math.max.apply(Math,y));
						mapView.fitExtent([min[0],min[1],max[0],max[1]],map.getSize());
					break;
					default:
						console.error('[bgMap] 不支援的操作 %s,msg=%s',msg.action,JSON.stringify(msg));
				}
			}
			var ev_open=function(){
				tryConnect=0;
			}
			var ev_close=function(){
				if(!code) return;
				if(tryConnect>=3){
					console.log('[bgMap] 無法與伺服器連線，放棄連線');
					alert('背景地圖無法與伺服器連線，請檢查網路連線，或者目前伺服器發生異常');
					return;
				}
				if(tryConnect){
					console.log('[bgMap] 無法與伺服器連線，嘗試次數：%d',tryConnect);
					setTimeout(bg.mapLoad,3000,code);
				}else{
					console.error('[bgMap] 失去連線，3 秒後重新連線');
					setTimeout(bg.mapLoad,3000,code);
				}
			}
			return function(data){
				if(!/^\w+$/.test(data)) return false;
				bg.mapUnload();
				code=data;
				bgMapLayer.style.display='';
				map.updateSize();
				tryConnect++;
				//socket=new WebSocket('ws://192.168.1.123:10080/'+code,'mapPoint');
				socket=new WebSocket('ws://g8v-a0000778.rhcloud.com:8000/'+code,'mapPoint');
				socket.addEventListener('message',ev_message);
				socket.addEventListener('open',ev_open);
				socket.addEventListener('close',ev_close);
			};
		})();
		bg.mapUnload=function(){
			if(!code) return;
			socket.close();
			socket=undefined;
			var k,o
			for(k in markList){
				o=markList[k];
				map.removeOverlay(o.layer);
				o.obj.$del();
				markList[k]=undefined;
				delete markList[k];
			}
			bgMapLayer.style.display='none';
			code=null;
			bg.mapAbortMark();
		};
		bg.mapMark=function(name,pos,mod,args,formServer){
			var mark=markList['p_'+name];
			if(!mark){
				var obj=markObj.cloneNode(true);
				obj.querySelector('.name').textContent=name;
				obj.addEventListener('click',function(){
					if(!(mark.module.length && g8v.module.hasOwnProperty(mark.module))) return;
					var mod=g8v.module[mark.module];
					mod.load.apply(mod,mark.args);
				});
				obj.addEventListener('contextmenu',function(e){
					e.preventDefault();
					if(confirm('確定要刪除標記點 '+name+' ?')) bg.mapUnMark(name);
				});
				mark=markList['p_'+name]={
					'name': name,
					'pos': pos,
					'module': mod || '',
					'args': args || [],
					'layer': new ol.Overlay({
						'position': transform(pos[0],pos[1]),
						'element': obj
					}),
					'obj': obj
				};
				map.addOverlay(mark.layer);
				obj=undefined;
				delete obj;
			}else{
				if(pos && pos.length===2){
					mark.pos=pos;
					mark.layer.setPosition(transform(pos[0],pos[1]))
				}
				if(mod && args){
					mark.module=mod;
					mark.args=args;
				}
			}
			if(!formServer)
				socket.send(JSON.stringify({
					'action': 'move',
					'name': name,
					'pos': mark.pos,
					'module': mark.mod,
					'args': mark.args
				}));
		};
		bg.mapUnMark=function(name,formServer){
			var mark=markList['p_'+name];
			if(!mark) return;
			map.removeOverlay(mark.layer);
			mark=markList['p_'+name]=undefined;
			delete mark,markList['p_'+name];
			if(!formServer)
				socket.send(JSON.stringify({
					'action': 'delete',
					'name': name
				}));
		};
		bg.mapStartMark=function(obj){
			if(!code){
				alert('標記地點需搭配背景地圖使用！');
				return;
			}
			if(waitMark){
				return false;
			}
			bgMapLayer.style.zIndex=2147483646;
			alert('請擊點欲標記的位址');
			waitMark=obj;
			return true;
		};
		bg.mapAbortMark=function(){
			bgMapLayer.style.zIndex=bgMapLayerIndex;
			waitMark=false;
		};
		g8v.windowOption.push(function(obj){
			return $.tag('span',{
				'textContent': '地',
				'addEventListener': ['click',function(e){
					if(!bg.mapStartMark(obj))
						alert('其他項目正在標記中');
				}]
			});
		});
		map.on('click',function(e){
			if(!waitMark) return;
			var name;
			while(!(name=prompt('請輸入標記點名稱：(輸入現存標記點名稱則表示更動該標記點)'))){
				if(name!==null) continue;
				bg.mapAbortMark();
				alert('取消標記');
				return;
			}
			if(markList['p_'+name] && !confirm('確定要更動標記點 '+name+' ?')) return;
			bg.mapMark(
				name,
				ol.proj.transform(e.coordinate,'EPSG:900913','EPSG:4326'),
				waitMark.module,
				(waitMark.title && waitMark.width && waitMark.height && waitMark.posX!=undefined && waitMark.posY!=undefined)?
					waitMark.args.concat([waitMark.title,waitMark.posX,waitMark.posY,waitMark.width,waitMark.height]):
					waitMark.args
			);
			bg.mapAbortMark();
		});
	})();

	var controlForm=$.tag('form',{'textContent':'設定背景：'});
	var inputBgData=controlForm.$add('input',{'type':'input','placeholder':'圖片、RGB、標記地圖'});
	controlForm.$add('input',{'type':'submit','value':'設定'});
	controlForm.addEventListener('submit',function(e){
		e.preventDefault();
		bg.load(inputBgData.value);
	});
	g8v.module.config.addItem(controlForm);

	document.head.$add('link',{
		'type': 'text/css',
		'rel': 'stylesheet',
		'href': (navigator.userAgent.indexOf('Firefox')!==-1)? 'module/bg_firefox.css':'module/bg.css'
	});
	g8v.module.bg=bg;
})();<|endoftext|>


<commit_before>var animationTime = 1;
var bst;
var treeGUI;
var loadingSequence;
var codeDisplayManager;
var markerInitialPosition;

function makeGUIUnEditable() {
    d3.select("#g_circles").selectAll('circle').on('click', null);
    d3.select("#g_labels").selectAll('text').on('click', null);
}

function findNodeInTreeEditor(binaryNode) {
    for (var i = 0; i < treeGUI.vis.length; i++) {
        if (treeGUI.vis[i].l === binaryNode.element) return treeGUI.vis[i];
    }
}

function createMarker(x, y) {
    markerInitialPosition = treeGUI.vis[0].p;
    /*if (x !== undefined && y !== undefined)
        markerInitialPosition = {x: x, y: y};*/
    d3.select("#treesvg").append('g').attr('class', 'markers').append('circle').attr('id', 'marker')
        .attr('cx', markerInitialPosition.x).attr('cy', markerInitialPosition.y).attr('r', 14)/*.attr('opacity', 0)*/;
}

function moveMarker(cx, cy) {
    return [{ e: $("#marker"), p: { x: cx - markerInitialPosition.x, y: cy - markerInitialPosition.y, opacity: 1 },
        o: { duration: animationTime} }];
}

function removeMarker() {$("#marker").remove()}

function init(rootLbl) {
    if (rootLbl == null) return;
    codeDisplayManager = new CodeDisplayManager("javascript", "binarySearchTree");
    bst = new BinarySearchTree();
    bst.insert(rootLbl);
    treeGUI = new tree(rootLbl);

    makeGUIUnEditable();
}

function visualizeInsert(lbl) {
    function getVertixMarkAnimation(n) {
        var point = findNodeInTreeEditor(n).p;
        return moveMarker(point.x, point.y);
    }

    function insert(x, t) {
        var hasBeenInIf = false;
        if (t !== null) {
            appendAnimation(null, getVertixMarkAnimation(t), codeDisplayManager);
        }
        else {
            var p = treeGUI.getFutureLeafPositionHelper(findNodeInTreeEditor(parent).v, direction);
            var tmp = moveMarker(p.x, p.y);
            appendAnimation(null, tmp, codeDisplayManager);
        }
        appendCodeLines([0], codeDisplayManager);

        if (t === null && !hasBeenInIf) {
            hasBeenInIf = true;
            t = new BinaryNode(x);

            // Following line of animation does nothing in particular.
            // It is a dummy animation in order to access and utilize onComplete and onReverseComplete
            var tmp = [{e: $("#marker"), p: {attr:{stroke: "red"}}, o:{duration: animationTime}}];

            tmp[0].p.onComplete = function() {
                var parentInGUI = findNodeInTreeEditor(parent);
                treeGUI.addLeaf(parentInGUI.v, direction, lbl);
                //$("#marker").animate()
            };
            tmp[0].p.onReverseComplete = function() {
                var parentInGUI = findNodeInTreeEditor(parent);
                for(var i = 0; i < parentInGUI.c.length; i++) {
                    if(parentInGUI.c[i].d === direction) {
                        treeGUI.removeLeaf(parentInGUI.c[i].v);
                    }
                }
                t = null;
            };

            appendAnimation(1, tmp, codeDisplayManager);
            appendCodeLines([2, 4, 6], codeDisplayManager);
        }

        else if (x - t.getElement() < 0 && !hasBeenInIf) {
            hasBeenInIf = true, direction = "left", parent = t;
            appendCodeLines([2, 3], codeDisplayManager);
            t.setLeft(insert(x, t.getLeft()));
            appendCodeLines([4, 6], codeDisplayManager);
        }

        else if (x - t.getElement() > 0 && !hasBeenInIf) {
            hasBeenInIf = true, direction = "right", parent = t;
            appendCodeLines([2, 4, 5], codeDisplayManager);
            t.setRight(insert(x, t.getRight()));
            appendCodeLines([6], codeDisplayManager);
        }

        else if(!hasBeenInIf) {
            hasBeenInIf = true;
            //throw { name: "DuplicateItemException", message: "Duplicate items are not allowed" };
            var animation = [{ e: $("#graphics, .treeEditor svg"), p: {"background-color": "#ff0000", opacity: 0.5}, o: { duration: animationTime/2} }];
            animation.push({ e: $("#graphics, .treeEditor svg"), p: {"background-color": "#fcfcfc", opacity: 1}, o: { duration: animationTime/2} });
            appendAnimation(7, animation, codeDisplayManager);
            return t;
        }
        appendCodeLines([8], codeDisplayManager);
        return t;
    }

    removeMarker();
    createMarker();
    codeDisplayManager.loadFunctions("insertNode");
    codeDisplayManager.changeFunction("insertNode");
    var parent = bst.root, direction = null;
    insert(lbl, bst.root);
    makeGUIUnEditable();
}

function visualizeSearch(lbl) {
    function getVertixMarkAnimation(n) {
        var point = findNodeInTreeEditor(n).p;
        return moveMarker(point.x, point.y);
    }
    removeMarker();
    codeDisplayManager.loadFunctions("findNode");
    codeDisplayManager.changeFunction("findNode");
    var t = bst.root;
    createMarker();
    while (t !== null) {
        var hasBeenInIf = false;
        appendCodeLines([0], codeDisplayManager);
        appendCodeLines([1], codeDisplayManager);
        if (lbl - t.getElement() < 0) {
            t = t.getLeft();
            if (t !== null) {
                appendAnimation(2, getVertixMarkAnimation(t), codeDisplayManager);
            }
            appendCodeLines([3, 5, 7], codeDisplayManager);
            hasBeenInIf = true;
        }
        else if (lbl - t.getElement() > 0 && !hasBeenInIf) {
            appendCodeLines([3], codeDisplayManager);
            t = t.getRight();
            if (t !== null) {
                appendAnimation(4, getVertixMarkAnimation(t), codeDisplayManager);
            }
            appendCodeLines([5, 7], codeDisplayManager);
            hasBeenInIf = true;
        }
        else if(!hasBeenInIf) {
            appendCodeLines([3, 5], codeDisplayManager);
            var animation = [{ e: $("#marker"), p: {stroke: "#00cd00", "stroke-width": 5}, o: { duration: animationTime} }];
            appendAnimation(6, animation, codeDisplayManager);
            return t; // Match
        }
    }

    var animation = [{ e: $("#graphics, .treeEditor svg"), p: {"background-color": "#ff0000", opacity: 0.5}, o: { duration: animationTime/2} }];
    animation.push({ e: $("#graphics, .treeEditor svg"), p: {"background-color": "#fcfcfc", opacity: 1}, o: { duration: animationTime/2} });
    appendAnimation(9, animation, codeDisplayManager);
    makeGUIUnEditable();
    return null; // Not Found
}

function visualizeRemove(lbl) {
    function remove(x, t) {
        if (parent != bst.root) {
            var point = findNodeInTreeEditor(parent).p;
            $("#g_lines line").each(function () {
                if (point.x == $(this).attr("x2") && point.y == $(this).attr("y2")) {
                    loadingSequence.push({e: $(this), p: {stroke: "#ffff00"}, o: {duration: animationTime}});
                }
            });
        }

        if (t === null) {
        }//TODO: give error
        if (x - t.getElement() < 0) {
            parent = t, direction = 'left';
            t.setLeft(remove(x, t.getLeft()));
        }
        else if (x - t.getElement() > 0) {
            parent = t, direction = 'right';
            t.setRight(remove(x, t.getRight()));
        }
        else if (t.getLeft() !== null && t.getRight() !== null) { // Two children
            t.setElement(bst.findMinNode(t.getRight()).getElement()); // Bytter verdiene på elementene
            t.setRight(bst.removeMinNode(t.getRight())); // Fjerner noden den har bytta med, og setter
        }
        else {
            if (t.getLeft() !== null) {
                parent = t, direction = 'left';
                t = t.getLeft();
            }
            else {
                parent = t, direction = 'right';
                t = t.getRight();
            }
        }
        return t;
    }
    removeMarker();
    codeDisplayManager.loadFunctions("removeNode");
    codeDisplayManager.changeFunction("removeNode");
    var parent = bst.root, direction = null;
    remove(lbl, bst.root);
    makeGUIUnEditable();
}

init(50);
/*
setTimeout(function(){
    visualizeInsert(90);
    setTimeout(function() {
        visualizeInsert(30);
    }, 3000)
}, 3000);
*/
<commit_msg>Added codehighlighting and marker movement for removeNode visualization in BinarySearchTree.<commit_after>var animationTime = 1;
var bst;
var treeGUI;
var loadingSequence;
var codeDisplayManager;
var markerInitialPosition;

function makeGUIUnEditable() {
    d3.select("#g_circles").selectAll('circle').on('click', null);
    d3.select("#g_labels").selectAll('text').on('click', null);
}

function findNodeInTreeEditor(binaryNode) {
    for (var i = 0; i < treeGUI.vis.length; i++) {
        if (treeGUI.vis[i].l === binaryNode.element) return treeGUI.vis[i];
    }
}

function createMarker(x, y) {
    markerInitialPosition = treeGUI.vis[0].p;
    /*if (x !== undefined && y !== undefined)
        markerInitialPosition = {x: x, y: y};*/
    d3.select("#treesvg").append('g').attr('class', 'markers').append('circle').attr('id', 'marker')
        .attr('cx', markerInitialPosition.x).attr('cy', markerInitialPosition.y).attr('r', 14)/*.attr('opacity', 0)*/;
}

function moveMarker(cx, cy) {
    return [{ e: $("#marker"), p: { x: cx - markerInitialPosition.x, y: cy - markerInitialPosition.y, opacity: 1 },
        o: { duration: animationTime} }];
}

function removeMarker() {$("#marker").remove()}

function getVertixMarkAnimation(n) {
    var point = findNodeInTreeEditor(n).p;
    return moveMarker(point.x, point.y);
}

function init(rootLbl) {
    if (rootLbl == null) return;
    codeDisplayManager = new CodeDisplayManager("javascript", "binarySearchTree");
    bst = new BinarySearchTree();
    bst.insert(rootLbl);
    treeGUI = new tree(rootLbl);

    makeGUIUnEditable();
}

function visualizeInsert(lbl) {
    function insert(x, t) {
        var hasBeenInIf = false;
        if (t !== null) {
            appendAnimation(null, getVertixMarkAnimation(t), codeDisplayManager);
        }
        else {
            var p = treeGUI.getFutureLeafPositionHelper(findNodeInTreeEditor(parent).v, direction);
            var tmp = moveMarker(p.x, p.y);
            appendAnimation(null, tmp, codeDisplayManager);
        }
        appendCodeLines([0], codeDisplayManager);

        if (t === null && !hasBeenInIf) {
            hasBeenInIf = true;
            t = new BinaryNode(x);

            // Following line of animation does nothing in particular.
            // It is a dummy animation in order to access and utilize onComplete and onReverseComplete
            var tmp = [{e: $("#marker"), p: {attr:{stroke: "red"}}, o:{duration: animationTime}}];

            tmp[0].p.onComplete = function() {
                var parentInGUI = findNodeInTreeEditor(parent);
                treeGUI.addLeaf(parentInGUI.v, direction, lbl);
                //$("#marker").animate()
            };
            tmp[0].p.onReverseComplete = function() {
                var parentInGUI = findNodeInTreeEditor(parent);
                for(var i = 0; i < parentInGUI.c.length; i++) {
                    if(parentInGUI.c[i].d === direction) {
                        treeGUI.removeLeaf(parentInGUI.c[i].v);
                    }
                }
                t = null;
            };

            appendAnimation(1, tmp, codeDisplayManager);
            appendCodeLines([2, 4, 6], codeDisplayManager);
        }

        else if (x - t.getElement() < 0 && !hasBeenInIf) {
            hasBeenInIf = true, direction = "left", parent = t;
            appendCodeLines([2, 3], codeDisplayManager);
            t.setLeft(insert(x, t.getLeft()));
            appendCodeLines([4, 6], codeDisplayManager);
        }

        else if (x - t.getElement() > 0 && !hasBeenInIf) {
            hasBeenInIf = true, direction = "right", parent = t;
            appendCodeLines([2, 4, 5], codeDisplayManager);
            t.setRight(insert(x, t.getRight()));
            appendCodeLines([6], codeDisplayManager);
        }

        else if(!hasBeenInIf) {
            hasBeenInIf = true;
            //throw { name: "DuplicateItemException", message: "Duplicate items are not allowed" };
            var animation = [{ e: $("#graphics, .treeEditor svg"), p: {"background-color": "#ff0000", opacity: 0.5}, o: { duration: animationTime/2} }];
            animation.push({ e: $("#graphics, .treeEditor svg"), p: {"background-color": "#fcfcfc", opacity: 1}, o: { duration: animationTime/2} });
            appendAnimation(7, animation, codeDisplayManager);
            return t;
        }
        appendCodeLines([8], codeDisplayManager);
        return t;
    }

    removeMarker();
    createMarker();
    codeDisplayManager.loadFunctions("insertNode");
    codeDisplayManager.changeFunction("insertNode");
    var parent = bst.root, direction = null;
    insert(lbl, bst.root);
    makeGUIUnEditable();
}

function visualizeSearch(lbl) {
    removeMarker();
    codeDisplayManager.loadFunctions("findNode");
    codeDisplayManager.changeFunction("findNode");
    var t = bst.root;
    createMarker();
    while (t !== null) {
        var hasBeenInIf = false;
        appendCodeLines([0], codeDisplayManager);
        appendCodeLines([1], codeDisplayManager);
        if (lbl - t.getElement() < 0) {
            t = t.getLeft();
            if (t !== null) {
                appendAnimation(2, getVertixMarkAnimation(t), codeDisplayManager);
            }
            appendCodeLines([3, 5, 7], codeDisplayManager);
            hasBeenInIf = true;
        }
        else if (lbl - t.getElement() > 0 && !hasBeenInIf) {
            appendCodeLines([3], codeDisplayManager);
            t = t.getRight();
            if (t !== null) {
                appendAnimation(4, getVertixMarkAnimation(t), codeDisplayManager);
            }
            appendCodeLines([5, 7], codeDisplayManager);
            hasBeenInIf = true;
        }
        else if(!hasBeenInIf) {
            appendCodeLines([3, 5], codeDisplayManager);
            var animation = [{ e: $("#marker"), p: {stroke: "#00cd00", "stroke-width": 5}, o: { duration: animationTime} }];
            appendAnimation(6, animation, codeDisplayManager);
            return t; // Match
        }
    }

    var animation = [{ e: $("#graphics, .treeEditor svg"), p: {"background-color": "#ff0000", opacity: 0.5}, o: { duration: animationTime/2} }];
    animation.push({ e: $("#graphics, .treeEditor svg"), p: {"background-color": "#fcfcfc", opacity: 1}, o: { duration: animationTime/2} });
    appendAnimation(9, animation, codeDisplayManager);
    makeGUIUnEditable();
    return null; // Not Found
}

function visualizeRemove(lbl) {
    function remove(x, t) {
        hasBeenInIf = false;
        if (t === null) {
            //TODO: give error
        } else {
            appendAnimation(null, getVertixMarkAnimation(t), codeDisplayManager);
        }

        appendCodeLines([2], codeDisplayManager);
        if (x - t.getElement() < 0) {
            hasBeenInIf = true, parent = t, direction = 'left';
            appendCodeLines([3], codeDisplayManager);
            t.setLeft(remove(x, t.getLeft()));
            appendCodeLines([4, 6, 9], codeDisplayManager);
        }
        else if (x - t.getElement() > 0) {
            hasBeenInIf = true, parent = t, direction = 'right';
            appendCodeLines([4, 5], codeDisplayManager);
            t.setRight(remove(x, t.getRight()));
            appendCodeLines([6, 9], codeDisplayManager);

        }
        else if (t.getLeft() !== null && t.getRight() !== null) { // Two children
            hasBeenInIf = true;
            appendCodeLines([6, 7, 8], codeDisplayManager);
            t.setElement(bst.findMinNode(t.getRight()).getElement()); // Bytter verdiene på elementene
            t.setRight(bst.removeMinNode(t.getRight())); // Fjerner noden den har bytta med, og setter
            appendCodeLines([9], codeDisplayManager);
        }
        else {
            appendCodeLines([10, 11], codeDisplayManager);
            hasBeenInIf = true;
            if (t.getLeft() !== null) {
                parent = t, direction = 'left';
                t = t.getLeft();
            }
            else {
                parent = t, direction = 'right';
                t = t.getRight();
            }
        }
        appendCodeLines([12], codeDisplayManager);
        return t;
    }
    removeMarker();
    createMarker();
    codeDisplayManager.loadFunctions("removeNode");
    codeDisplayManager.changeFunction("removeNode");
    var parent = bst.root, direction = null, hasBeenInIf = false;
    remove(lbl, bst.root);
    makeGUIUnEditable();
}

init(50);
/*
setTimeout(function(){
    visualizeInsert(90);
    setTimeout(function() {
        visualizeInsert(30);
    }, 3000)
}, 3000);
*/
<|endoftext|>


<commit_before>/* global window, module, localStorage, navigator, console */
/* jshint -W078 */
'use strict';

var PushHelper = (function PushHelper() {

  // Simple check if we have the api enabled
  // otherwise throw an error
  if (typeof navigator.push === undefined) {
    throw new Error('No Simple Push API available');
  }

  // Array to map in memory an structure containing the channels that
  // we listen for and the actual endpoints.
  //
  // CHANNELS = [
  //    {
  //      'name': 'channelName1',
  //      'endPoint': '<unique endpoint>'
  //    },
  //    {
  //      'name': 'channelName2',
  //      'endPoint': '<unique endpoint>'
  //    }
  //    ...
  // ];
  //
  var CHANNELS = [];

  var pendingRegisters = 0;
  var onRegister = null;

  var errors = [];

  var DEBUG = false;

  // Try to fetch the specific channel from the storage.
  // Currently using localStorage and making this sync, but in the
  // future this should change to any kind of async storage.
  //
  // @param channelName String
  var getChannelFromStore = function getChannelFromStore(channelName) {
    if (DEBUG) {
      console.log('Do I have channel ' + channelName);
    }

    var value = localStorage[channelName];
    return value ? JSON.parse(value) : null;
  };

  // Save the current channel to storage.
  // Right now using localStorage, we should move this to async storage.
  //
  // @param channel Object Channel object
  var saveChannelToStore = function saveChannelToStore(channel) {
    var simpleChannel = {
      name: channel.name,
      endPoint: channel.endPoint
    };
    localStorage[channel.name] = JSON.stringify(simpleChannel);
  };

  // Setup a new channel from which we want to receive notifications.
  //
  // @param channelName String A identifier for this channel.
  // @param resolverFn Function Used to fetch the info associated
  //        to an specific version that will come in the push notification.
  //        Optional, if no specified, will go directly to the data callback.
  //        This function must have a unique parameter, that will be a function,
  //        to be call once we went to the our server for the data.
  // @param dataFn Function Will be called once the data has been resolver,
  //        if needed. Mandatory
  var listen = function listen(channelName, resolverFn, dataFn) {
    if (!channelName || !dataFn) {
      throw new Error('Invalid parameters please check documentation');
    }
    var channel = getChannelFromStore(channelName);

    if (!channel) {
      channel = {
        'endPoint': null
      };
    }

    channel.resolver = resolverFn;
    channel.data = dataFn;
    channel.name = channelName;

    CHANNELS.push(channel);
    if (DEBUG) {
      console.log('Channels is ' + CHANNELS);
    }
  };

  // Check if the channels that we are listening are not registered
  // in our app, in that case perform the register (even for multiple)
  // channels, and send the information of the callbacks to the register
  // callback to perform any storing operation in a different place than
  // the phone.
  //
  // @param registerFn Function Will be called if we need to register new
  //        channels (end points) in our server side. First param is an error
  //        object, second parameter is the result.
  var register = function register(registerFn) {
    onRegister = registerFn;
    CHANNELS.forEach(function onChannel(channel) {
      if (!channel.endPoint) {
        doRegister(channel);
      } else if (DEBUG) {
        console.log('We already have the channel ' + channel.endPoint);
      }
    });
  };

  // Register a new channel for a new endpoint
  var doRegister = function doRegister(channel) {
    if (DEBUG) {
      console.log('Registering channel ' + channel.name + ' (performing navigator.push.register)');
    }
    var req = navigator.push.register();
    pendingRegisters++;

    req.onsuccess = function onSuccess() {
      if (DEBUG) {
        console.log('Successfully registered channel ' + channel.name);
      }
      var endPoint = req.result;
      channel.endPoint = endPoint;
      saveChannelToStore(channel);
      pendingRegisters--;
      checkRegister();
    };

    req.onerror = function onError() {
      if (DEBUG) {
        console.log('Error registering channel ' + channel.name);
      }
      errors.push({
        'type': 'register',
        'data': channel.name
      });
      pendingRegisters--;
      checkRegister();
    };
  };

  // Check if we have pending registers operations, if not
  // call the onRegister callback if present.
  var checkRegister = function checkRegister() {
    if (pendingRegisters === 0 && onRegister !== null) {
      if (DEBUG) {
        console.log('Finished the registering, telling the server ' + JSON.stringify(CHANNELS));
      }
      onRegister(CHANNELS);
    }
  };

  // Start the process, will listen for any incoming push notifications.
  var init = function init() {
    window.navigator.mozSetMessageHandler('push', function(evt) {
      var endPoint = evt.pushEndpoint;
      var version = evt.version;
      console.log('Receivied a push message: ' + endPoint + ' : ' + version);

      var channel = CHANNELS.find(function searchChannel(el) {
        if (el.endPoint === endPoint) {
          return el;
        }
      });

      // Warn and don't err, if channel not registered
      if (channel === undefined) {
        console.warn('Unknown channel via endpoint ' + endPoint + ' with version ' + version);
        return;
      }

      if (channel.resolver) {
        channel.resolver(version, endPoint, channel.data);
      } else {
        channel.data();
      }
    });
  };

  return {
    'listen': listen,
    'register': register,
    'init': init,
    set debug(bol) {
      DEBUG = !!bol;
    }
  };

})();

if (typeof module === undefined && window) {
  window.PushHelper = PushHelper;
} else if (!window) {
  module.exports = PushHelper;
}
<commit_msg>Calling the register callback when we do have the channels already saved<commit_after>/* global window, module, localStorage, navigator, console */
/* jshint -W078 */
'use strict';

var PushHelper = (function PushHelper() {

  // Simple check if we have the api enabled
  // otherwise throw an error
  if (typeof navigator.push === undefined) {
    throw new Error('No Simple Push API available');
  }

  // Array to map in memory an structure containing the channels that
  // we listen for and the actual endpoints.
  //
  // CHANNELS = [
  //    {
  //      'name': 'channelName1',
  //      'endPoint': '<unique endpoint>'
  //    },
  //    {
  //      'name': 'channelName2',
  //      'endPoint': '<unique endpoint>'
  //    }
  //    ...
  // ];
  //
  var CHANNELS = [];

  var pendingRegisters = 0;
  var onRegister = null;

  var errors = [];

  var DEBUG = false;

  // Try to fetch the specific channel from the storage.
  // Currently using localStorage and making this sync, but in the
  // future this should change to any kind of async storage.
  //
  // @param channelName String
  var getChannelFromStore = function getChannelFromStore(channelName) {
    if (DEBUG) {
      console.log('Do I have channel ' + channelName);
    }

    var value = localStorage[channelName];
    return value ? JSON.parse(value) : null;
  };

  // Save the current channel to storage.
  // Right now using localStorage, we should move this to async storage.
  //
  // @param channel Object Channel object
  var saveChannelToStore = function saveChannelToStore(channel) {
    var simpleChannel = {
      name: channel.name,
      endPoint: channel.endPoint
    };
    localStorage[channel.name] = JSON.stringify(simpleChannel);
  };

  // Setup a new channel from which we want to receive notifications.
  //
  // @param channelName String A identifier for this channel.
  // @param resolverFn Function Used to fetch the info associated
  //        to an specific version that will come in the push notification.
  //        Optional, if no specified, will go directly to the data callback.
  //        This function must have a unique parameter, that will be a function,
  //        to be call once we went to the our server for the data.
  // @param dataFn Function Will be called once the data has been resolver,
  //        if needed. Mandatory
  var listen = function listen(channelName, resolverFn, dataFn) {
    if (!channelName || !dataFn) {
      throw new Error('Invalid parameters please check documentation');
    }
    var channel = getChannelFromStore(channelName);

    if (!channel) {
      channel = {
        'endPoint': null
      };
    }

    channel.resolver = resolverFn;
    channel.data = dataFn;
    channel.name = channelName;

    CHANNELS.push(channel);
    if (DEBUG) {
      console.log('Channels is ' + CHANNELS);
    }
  };

  // Check if the channels that we are listening are not registered
  // in our app, in that case perform the register (even for multiple)
  // channels, and send the information of the callbacks to the register
  // callback to perform any storing operation in a different place than
  // the phone.
  //
  // @param registerFn Function Will be called if we need to register new
  //        channels (end points) in our server side. First param is an error
  //        object, second parameter is the result.
  var register = function register(registerFn) {
    onRegister = registerFn;
    CHANNELS.forEach(function onChannel(channel) {
      if (!channel.endPoint) {
        doRegister(channel);
      } else if (DEBUG) {
        console.log('We already have the channel ' + channel.endPoint);
	checkRegister();
      }
    });
  };

  // Register a new channel for a new endpoint
  var doRegister = function doRegister(channel) {
    if (DEBUG) {
      console.log('Registering channel ' + channel.name + ' (performing navigator.push.register)');
    }
    var req = navigator.push.register();
    pendingRegisters++;

    req.onsuccess = function onSuccess() {
      if (DEBUG) {
        console.log('Successfully registered channel ' + channel.name);
      }
      var endPoint = req.result;
      channel.endPoint = endPoint;
      saveChannelToStore(channel);
      pendingRegisters--;
      checkRegister();
    };

    req.onerror = function onError() {
      if (DEBUG) {
        console.log('Error registering channel ' + channel.name);
      }
      errors.push({
        'type': 'register',
        'data': channel.name
      });
      pendingRegisters--;
      checkRegister();
    };
  };

  // Check if we have pending registers operations, if not
  // call the onRegister callback if present.
  var checkRegister = function checkRegister() {
    if (pendingRegisters === 0 && onRegister !== null) {
      if (DEBUG) {
        console.log('Finished the registering, telling the server ' + JSON.stringify(CHANNELS));
      }
      onRegister(CHANNELS);
    }
  };

  // Start the process, will listen for any incoming push notifications.
  var init = function init() {
    window.navigator.mozSetMessageHandler('push', function(evt) {
      var endPoint = evt.pushEndpoint;
      var version = evt.version;
      console.log('Receivied a push message: ' + endPoint + ' : ' + version);

      var channel = CHANNELS.find(function searchChannel(el) {
        if (el.endPoint === endPoint) {
          return el;
        }
      });

      // Warn and don't err, if channel not registered
      if (channel === undefined) {
        console.warn('Unknown channel via endpoint ' + endPoint + ' with version ' + version);
        return;
      }

      if (channel.resolver) {
        channel.resolver(version, endPoint, channel.data);
      } else {
        channel.data();
      }
    });
  };

  return {
    'listen': listen,
    'register': register,
    'init': init,
    set debug(bol) {
      DEBUG = !!bol;
    }
  };

})();

if (typeof module === undefined && window) {
  window.PushHelper = PushHelper;
} else if (!window) {
  module.exports = PushHelper;
}
<|endoftext|>


<commit_before>'use strict'

var stringifySafe = require('fast-safe-stringify')
var EventEmitter = require('events').EventEmitter
var fs = require('fs')
var pump = require('pump')
var flatstr = require('flatstr')
var pretty = require('./pretty')
var events = require('./lib/events')
var levels = require('./lib/levels')
var tools = require('./lib/tools')
var serializers = require('./lib/serializers')

var LOG_VERSION = 1

var defaultOptions = {
  safe: true,
  name: undefined,
  serializers: {},
  timestamp: true,
  slowtime: false,
  extreme: false,
  level: 'info',
  levelVal: undefined,
  prettyPrint: false,
  onTerminated: function (eventName, err) {
    if (err) return process.exit(1)
    process.exit(0)
  },
  enabled: true,
  messageKey: 'msg'
}

var pinoPrototype = Object.create(EventEmitter.prototype, {
  silent: {
    value: tools.noop,
    enumerable: true
  },
  stream: {
    value: process.stdout,
    writable: true
  }
})

var levelMethods = ['fatal', 'error', 'warn', 'info', 'debug', 'trace']
levelMethods.forEach(function (m) {
  Object.defineProperty(pinoPrototype, m, {
    value: tools.genLog(levels.levels[m]),
    enumerable: true,
    writable: true
  })
})

function getLevelVal () {
  return this._levelVal
}
function setLevelVal (num) {
  if (typeof num === 'string') { return this._setLevel(num) }

  if (this.emit) {
    this.emit('level-change', this.levels.labels[num], num, this.levels.labels[this._levelVal], this._levelVal)
  }

  this._levelVal = num

  for (var key in this.levels.values) {
    if (num > this.levels.values[key]) {
      this[key] = tools.noop
      continue
    }
    this[key] = levels.isStandardLevel(key) ? pinoPrototype[key] : tools.genLog(this.levels.values[key])
  }
}
Object.defineProperty(pinoPrototype, 'levelVal', {
  get: getLevelVal,
  set: setLevelVal
})

function _setLevel (level) {
  if (typeof level === 'number') {
    if (!isFinite(level)) {
      throw Error('unknown level ' + level)
    }
    level = this.levels.labels[level]
  }

  if (!this.levels.values[level]) {
    throw Error('unknown level ' + level)
  }
  this.levelVal = this.levels.values[level]
}
Object.defineProperty(pinoPrototype, '_setLevel', {
  value: _setLevel
})

Object.defineProperty(pinoPrototype, '_getLevel', {
  value: function _getLevel (level) {
    return this.levels.labels[this.levelVal]
  }
})

Object.defineProperty(pinoPrototype, 'level', {
  get: pinoPrototype._getLevel,
  set: pinoPrototype._setLevel
})

Object.defineProperty(pinoPrototype, '_lscache', {
  value: tools.copy({}, levels.lscache)
})

Object.defineProperty(
  pinoPrototype,
  'LOG_VERSION',
  {value: LOG_VERSION}
)

function asJson (obj, msg, num) {
  if (!msg && obj instanceof Error) {
    msg = obj.message
  }
  var data = this._baseLog + this._lscache[num] + this.time()
  // to catch both null and undefined
  /* eslint-disable eqeqeq */
  if (msg != undefined) {
    data += this.messageKeyString + JSON.stringify('' + msg)
  }
  // we need the child bindings added to the output first so that logged
  // objects can take precedence when JSON.parse-ing the resulting log line
  data = data + this.chindings
  var value
  if (obj) {
    if (obj.stack) {
      data += ',"type":"Error","stack":' + this.stringify(obj.stack)
    }
    for (var key in obj) {
      value = obj[key]
      if ((!obj.hasOwnProperty || obj.hasOwnProperty(key)) && value !== undefined) {
        value = this.stringify(this.serializers[key] ? this.serializers[key](value) : value)
        if (value !== undefined) {
          data += ',"' + key + '":' + value
        }
      }
    }
  }
  return data + this.end
}
Object.defineProperty(pinoPrototype, 'asJson', {
  enumerable: true,
  value: asJson
})

function child (bindings) {
  if (!bindings) {
    throw Error('missing bindings for child Pino')
  }

  var data = ','
  var value
  var key
  for (key in bindings) {
    value = bindings[key]
    if (key !== 'level' && key !== 'serializers' && bindings.hasOwnProperty(key) && value !== undefined) {
      value = this.serializers[key] ? this.serializers[key](value) : value
      data += '"' + key + '":' + this.stringify(value) + ','
    }
  }
  data = this.chindings + data.substr(0, data.length - 1)

  var opts = {
    level: bindings.level || this.level,
    levelVal: levels.isStandardLevelVal(this.levelVal) ? undefined : this.levelVal,
    serializers: bindings.hasOwnProperty('serializers') ? Object.assign({}, this.serializers, bindings.serializers) : this.serializers,
    stringify: this.stringify,
    end: this.end,
    name: this.name,
    timestamp: this.timestamp,
    slowtime: this.slowtime,
    chindings: data,
    cache: this.cache,
    formatOpts: this.formatOpts,
    messageKey: this.messageKey,
    messageKeyString: this.messageKeyString
  }

  var _child = Object.create(this)
  _child.stream = this.stream
  tools.applyOptions.call(_child, opts)
  return _child
}
Object.defineProperty(pinoPrototype, 'child', {
  enumerable: true,
  value: child
})

function pinoWrite (obj, msg, num) {
  var s = this.asJson(obj, msg, num)
  if (!this.cache) {
    this.stream.write(flatstr(s))
    return
  }

  this.cache.buf += s
  if (this.cache.buf.length > this.cache.size) {
    this.stream.write(flatstr(this.cache.buf))
    this.cache.buf = ''
  }
}
Object.defineProperty(pinoPrototype, 'write', {
  value: pinoWrite
})

function flush () {
  if (!this.cache) {
    return
  }

  this.stream.write(flatstr(this.cache.buf))
  this.cache.buf = ''
}
Object.defineProperty(pinoPrototype, 'flush', {
  enumerable: true,
  value: flush
})

function addLevel (name, lvl) {
  if (this.levels.values.hasOwnProperty(name)) return false
  if (this.levels.labels.hasOwnProperty(lvl)) return false
  this.levels.values[name] = lvl
  this.levels.labels[lvl] = name
  this._lscache[lvl] = flatstr('"level":' + Number(lvl))
  this[name] = tools.genLog(lvl)
  return true
}
Object.defineProperty(pinoPrototype, 'addLevel', {
  enumerable: true,
  value: addLevel
})

function pino (opts, stream) {
  var iopts = opts
  var istream = stream
  if (iopts && (iopts.writable || iopts._writableState)) {
    istream = iopts
    iopts = defaultOptions
  }
  iopts = Object.assign({}, defaultOptions, iopts)
  if (iopts.extreme && iopts.prettyPrint) throw Error('cannot enable pretty print in extreme mode')
  istream = istream || process.stdout
  if (iopts.prettyPrint) {
    var prettyOpts = Object.assign({ messageKey: iopts.messageKey }, iopts.prettyPrint)
    var pstream = pretty(prettyOpts)
    var origStream = istream
    pump(pstream, origStream, function (err) {
      if (err) instance.emit('error', err)
    })
    istream = pstream
  }

  // internal options
  iopts.stringify = iopts.safe ? stringifySafe : JSON.stringify
  iopts.formatOpts = {lowres: true}
  iopts.messageKeyString = ',' + JSON.stringify('' + iopts.messageKey) + ':'
  iopts.end = ',"v":' + LOG_VERSION + '}\n'
  iopts.cache = !iopts.extreme ? null : {
    size: 4096,
    buf: ''
  }
  iopts.chindings = ''

  if (iopts.enabled === false) {
    iopts.level = 'silent'
  }

  var instance = Object.create(pinoPrototype)
  instance.stream = istream
  tools.defineLevelsProperty(instance)
  tools.applyOptions.call(instance, iopts)
  if (iopts.cache) setTimeout(waitForFDSettle, 100)

  var settleTries = 0
  function waitForFDSettle () {
    var isBlockable = tools.streamIsBlockable(istream)
    if (isBlockable === false && settleTries > 10) {
      return instance.emit('error', Error('stream must have a file descriptor in extreme mode'))
    } else if (isBlockable === true) {
      return events(instance, extremeModeExitHandler)
    }
    settleTries += 1
    setTimeout(waitForFDSettle, 100)
  }

  function extremeModeExitHandler () {
    var buf = iopts.cache.buf
    if (buf) {
      // We need to block the process exit long enough to flush the buffer
      // to the destination stream. We do that by forcing a synchronous
      // write directly to the stream's file descriptor.
      var fd = (istream.fd) ? istream.fd : istream._handle.fd
      fs.writeSync(fd, buf)
    }
  }

  return instance
}

tools.defineLevelsProperty(pino)

module.exports = pino
module.exports.stdSerializers = {
  req: serializers.asReqValue,
  res: serializers.asResValue,
  err: serializers.asErrValue
}
module.exports.pretty = pretty
Object.defineProperty(
  module.exports,
  'LOG_VERSION',
  {value: LOG_VERSION, enumerable: true}
)
<commit_msg>string template ftw<commit_after>'use strict'

var stringifySafe = require('fast-safe-stringify')
var EventEmitter = require('events').EventEmitter
var fs = require('fs')
var pump = require('pump')
var flatstr = require('flatstr')
var pretty = require('./pretty')
var events = require('./lib/events')
var levels = require('./lib/levels')
var tools = require('./lib/tools')
var serializers = require('./lib/serializers')

var LOG_VERSION = 1

var defaultOptions = {
  safe: true,
  name: undefined,
  serializers: {},
  timestamp: true,
  slowtime: false,
  extreme: false,
  level: 'info',
  levelVal: undefined,
  prettyPrint: false,
  onTerminated: function (eventName, err) {
    if (err) return process.exit(1)
    process.exit(0)
  },
  enabled: true,
  messageKey: 'msg'
}

var pinoPrototype = Object.create(EventEmitter.prototype, {
  silent: {
    value: tools.noop,
    enumerable: true
  },
  stream: {
    value: process.stdout,
    writable: true
  }
})

var levelMethods = ['fatal', 'error', 'warn', 'info', 'debug', 'trace']
levelMethods.forEach(function (m) {
  Object.defineProperty(pinoPrototype, m, {
    value: tools.genLog(levels.levels[m]),
    enumerable: true,
    writable: true
  })
})

function getLevelVal () {
  return this._levelVal
}
function setLevelVal (num) {
  if (typeof num === 'string') { return this._setLevel(num) }

  if (this.emit) {
    this.emit('level-change', this.levels.labels[num], num, this.levels.labels[this._levelVal], this._levelVal)
  }

  this._levelVal = num

  for (var key in this.levels.values) {
    if (num > this.levels.values[key]) {
      this[key] = tools.noop
      continue
    }
    this[key] = levels.isStandardLevel(key) ? pinoPrototype[key] : tools.genLog(this.levels.values[key])
  }
}
Object.defineProperty(pinoPrototype, 'levelVal', {
  get: getLevelVal,
  set: setLevelVal
})

function _setLevel (level) {
  if (typeof level === 'number') {
    if (!isFinite(level)) {
      throw Error('unknown level ' + level)
    }
    level = this.levels.labels[level]
  }

  if (!this.levels.values[level]) {
    throw Error('unknown level ' + level)
  }
  this.levelVal = this.levels.values[level]
}
Object.defineProperty(pinoPrototype, '_setLevel', {
  value: _setLevel
})

Object.defineProperty(pinoPrototype, '_getLevel', {
  value: function _getLevel (level) {
    return this.levels.labels[this.levelVal]
  }
})

Object.defineProperty(pinoPrototype, 'level', {
  get: pinoPrototype._getLevel,
  set: pinoPrototype._setLevel
})

Object.defineProperty(pinoPrototype, '_lscache', {
  value: tools.copy({}, levels.lscache)
})

Object.defineProperty(
  pinoPrototype,
  'LOG_VERSION',
  {value: LOG_VERSION}
)

function asJson (obj, msg, num) {
  if (!msg && obj instanceof Error) {
    msg = obj.message
  }
  var data = this._baseLog + this._lscache[num] + this.time()
  // to catch both null and undefined
  /* eslint-disable eqeqeq */
  if (msg != undefined) {
    data += this.messageKeyString + JSON.stringify('' + msg)
  }
  // we need the child bindings added to the output first so that logged
  // objects can take precedence when JSON.parse-ing the resulting log line
  data = data + this.chindings
  var value
  if (obj) {
    if (obj.stack) {
      data += ',"type":"Error","stack":' + this.stringify(obj.stack)
    }
    for (var key in obj) {
      value = obj[key]
      if ((!obj.hasOwnProperty || obj.hasOwnProperty(key)) && value !== undefined) {
        value = this.stringify(this.serializers[key] ? this.serializers[key](value) : value)
        if (value !== undefined) {
          data += ',"' + key + '":' + value
        }
      }
    }
  }
  return data + this.end
}
Object.defineProperty(pinoPrototype, 'asJson', {
  enumerable: true,
  value: asJson
})

function child (bindings) {
  if (!bindings) {
    throw Error('missing bindings for child Pino')
  }

  var data = ','
  var value
  var key
  for (key in bindings) {
    value = bindings[key]
    if (key !== 'level' && key !== 'serializers' && bindings.hasOwnProperty(key) && value !== undefined) {
      value = this.serializers[key] ? this.serializers[key](value) : value
      data += '"' + key + '":' + this.stringify(value) + ','
    }
  }
  data = this.chindings + data.substr(0, data.length - 1)

  var opts = {
    level: bindings.level || this.level,
    levelVal: levels.isStandardLevelVal(this.levelVal) ? undefined : this.levelVal,
    serializers: bindings.hasOwnProperty('serializers') ? Object.assign({}, this.serializers, bindings.serializers) : this.serializers,
    stringify: this.stringify,
    end: this.end,
    name: this.name,
    timestamp: this.timestamp,
    slowtime: this.slowtime,
    chindings: data,
    cache: this.cache,
    formatOpts: this.formatOpts,
    messageKey: this.messageKey,
    messageKeyString: this.messageKeyString
  }

  var _child = Object.create(this)
  _child.stream = this.stream
  tools.applyOptions.call(_child, opts)
  return _child
}
Object.defineProperty(pinoPrototype, 'child', {
  enumerable: true,
  value: child
})

function pinoWrite (obj, msg, num) {
  var s = this.asJson(obj, msg, num)
  if (!this.cache) {
    this.stream.write(flatstr(s))
    return
  }

  this.cache.buf += s
  if (this.cache.buf.length > this.cache.size) {
    this.stream.write(flatstr(this.cache.buf))
    this.cache.buf = ''
  }
}
Object.defineProperty(pinoPrototype, 'write', {
  value: pinoWrite
})

function flush () {
  if (!this.cache) {
    return
  }

  this.stream.write(flatstr(this.cache.buf))
  this.cache.buf = ''
}
Object.defineProperty(pinoPrototype, 'flush', {
  enumerable: true,
  value: flush
})

function addLevel (name, lvl) {
  if (this.levels.values.hasOwnProperty(name)) return false
  if (this.levels.labels.hasOwnProperty(lvl)) return false
  this.levels.values[name] = lvl
  this.levels.labels[lvl] = name
  this._lscache[lvl] = flatstr('"level":' + Number(lvl))
  this[name] = tools.genLog(lvl)
  return true
}
Object.defineProperty(pinoPrototype, 'addLevel', {
  enumerable: true,
  value: addLevel
})

function pino (opts, stream) {
  var iopts = opts
  var istream = stream
  if (iopts && (iopts.writable || iopts._writableState)) {
    istream = iopts
    iopts = defaultOptions
  }
  iopts = Object.assign({}, defaultOptions, iopts)
  if (iopts.extreme && iopts.prettyPrint) throw Error('cannot enable pretty print in extreme mode')
  istream = istream || process.stdout
  if (iopts.prettyPrint) {
    var prettyOpts = Object.assign({ messageKey: iopts.messageKey }, iopts.prettyPrint)
    var pstream = pretty(prettyOpts)
    var origStream = istream
    pump(pstream, origStream, function (err) {
      if (err) instance.emit('error', err)
    })
    istream = pstream
  }

  // internal options
  iopts.stringify = iopts.safe ? stringifySafe : JSON.stringify
  iopts.formatOpts = {lowres: true}
  iopts.messageKeyString = `,"${iopts.messageKey}":`
  iopts.end = ',"v":' + LOG_VERSION + '}\n'
  iopts.cache = !iopts.extreme ? null : {
    size: 4096,
    buf: ''
  }
  iopts.chindings = ''

  if (iopts.enabled === false) {
    iopts.level = 'silent'
  }

  var instance = Object.create(pinoPrototype)
  instance.stream = istream
  tools.defineLevelsProperty(instance)
  tools.applyOptions.call(instance, iopts)
  if (iopts.cache) setTimeout(waitForFDSettle, 100)

  var settleTries = 0
  function waitForFDSettle () {
    var isBlockable = tools.streamIsBlockable(istream)
    if (isBlockable === false && settleTries > 10) {
      return instance.emit('error', Error('stream must have a file descriptor in extreme mode'))
    } else if (isBlockable === true) {
      return events(instance, extremeModeExitHandler)
    }
    settleTries += 1
    setTimeout(waitForFDSettle, 100)
  }

  function extremeModeExitHandler () {
    var buf = iopts.cache.buf
    if (buf) {
      // We need to block the process exit long enough to flush the buffer
      // to the destination stream. We do that by forcing a synchronous
      // write directly to the stream's file descriptor.
      var fd = (istream.fd) ? istream.fd : istream._handle.fd
      fs.writeSync(fd, buf)
    }
  }

  return instance
}

tools.defineLevelsProperty(pino)

module.exports = pino
module.exports.stdSerializers = {
  req: serializers.asReqValue,
  res: serializers.asResValue,
  err: serializers.asErrValue
}
module.exports.pretty = pretty
Object.defineProperty(
  module.exports,
  'LOG_VERSION',
  {value: LOG_VERSION, enumerable: true}
)
<|endoftext|>


<commit_before>/* global NGI, console */
/* jshint strict: false */
/* jshint expr: true */
/* jshint boss: true */

NGI.TreeView = (function() {

	function TreeViewItem(label) {

		this.element = document.createElement('div');

		this.label = document.createElement('label');
		this.label.textContent = label;
		this.element.appendChild(this.label);

		this.drawer = document.createElement('div');
		this.drawer.className = 'ngi-drawer';
		this.element.appendChild(this.drawer);

		this.caret = document.createElement('span');
		this.caret.className = 'ngi-caret';

		this.length = null;

		var collapsed = false;
		this.setCollapsed = function(newState) {
			if (collapsed = newState) {
				this.element.classList.add('ngi-collapsed');
				this.element.classList.remove('ngi-expanded');
			} else {
				this.element.classList.remove('ngi-collapsed');
				this.element.classList.add('ngi-expanded');
			}
		};
		this.toggle = function(e) {
			e.stopPropagation();
			this.setCollapsed(!collapsed);
		};
		this.caret.addEventListener('click', this.toggle.bind(this));

		var isCollapsible = false;
		this.makeCollapsible = function(collapsibleState, initialState) {
			if (isCollapsible == collapsibleState) {
				return;
			}
			if (isCollapsible = collapsibleState) {
				this.label.appendChild(this.caret);
				this.setCollapsed(initialState || false);
			} else {
				this.label.removeChild(this.caret);
			}
		}

		this.addChild = function(childItem, top) {
			if (!!top) {
				this.drawer.insertBefore(childItem.element, this.drawer.firstChild);
			} else {
				this.drawer.appendChild(childItem.element);
			}
		};

		this.removeChildren = function(className) {
			for (var i = this.drawer.childNodes.length - 1; i >= 0; i--) {
				var child = this.drawer.childNodes[i];
				if (child.classList.contains(className)) {
					this.drawer.removeChild(child);
				}
			}
		};

		this.destroy = function() {
			if (this.element.parentNode) {
				this.element.parentNode.removeChild(this.element);
			}
		};

		// Pill indicator
		var indicator = false;
		this.setIndicator = function(value) {
			if (indicator && typeof value !== 'number' && typeof value !== 'string') {
				indicator.parentNode.removeChild(indicator);
			} else {
				if (!indicator) {
					indicator = document.createElement('span');
					indicator.className = 'ngi-indicator';
					// indicator.innerHTML = value;
					this.label.appendChild(indicator);
				}
			}
		};

		// Annotations (controller names, custom and built-in directive names)
		var annotations = [];
		this.addAnnotation = function(name, type) {
			if (annotations.indexOf(name) < 0) {
				annotations.push(name);
			} else {
				return;
			}
			var span = document.createElement('span');
			span.className = 'ngi-annotation';
			span.textContent = name;
			switch(type) {
				case NGI.Service.DIR:
					span.classList.add('ngi-annotation-dir');
					break;
				case NGI.Service.BUILTIN:
					span.classList.add('ngi-annotation-builtin');
					break;
				case NGI.Service.CTRL:
					span.classList.add('ngi-annotation-ctrl');
					break;
			}
			this.label.appendChild(span);
		};

		// Model types
		var type = null;
		this.setType = function(newType) {
			if (type) {
				this.element.classList.remove(type);
			}
			this.element.classList.add(newType);
			type = newType;
		};

	}

	function TreeView() {}

	// Creates a TreeViewItem instance, with styling and metadata relevant for
	// AngularJS apps
	TreeView.appItem = function(label, node) {
		if (node === document) node = document.querySelector('html');
		var item = new TreeViewItem(label);
		item.node = node;
		item.element.className = 'ngi-app';
		return item;
	};

	// Creates a TreeViewItem instance, with styling and metadata relevant for
	// AngularJS scopes
	TreeView.scopeItem = function(label, depth, isIsolate) {
		var item = new TreeViewItem(label);
		item.element.className = 'ngi-scope';
		item.makeCollapsible(true, false);
		if (isIsolate) {
			item.element.classList.add('ngi-isolate-scope');
		}
		item.label.className = 'ngi-depth-' + depth.length;

		// Highlight DOM elements the scope is attached to when hovering the item
		// in the inspector
		item.label.addEventListener('mouseover', function(event) {
			var isCaret = event.target && event.target.classList.contains('ngi-caret');
			if ( item.node && !window.ngInspector.pane.isResizing && !isCaret) {
				var target = (item.node === document) ?
					document.querySelector('html') : item.node;
				// target.classList.add('ngi-highlight');
				NGI.Highlighter.hl(target);
			}
		});
		item.label.addEventListener('mouseout', function() {
			if (item.node) {
				NGI.Highlighter.clear();
			}
		});

		// console.log the DOM Node this scope is attached to
		item.label.addEventListener('click', function() {
			console.log(item.node);
		});

		return item;
	};

	// Creates a TreeViewItem instance, with styling and metadata relevant for
	// AngularJS models
	TreeView.modelItem = function(key, value, depth) {
		var item = new TreeViewItem(key + ':');
		item.element.className = 'ngi-model';
		item.label.className = 'ngi-depth-' + depth.length;

		item.label.addEventListener('click', function() {
			console.log(value);
		});

		return item;
	};

	return TreeView;

})();
<commit_msg>Replace use of innerHTML with textContent<commit_after>/* global NGI, console */
/* jshint strict: false */
/* jshint expr: true */
/* jshint boss: true */

NGI.TreeView = (function() {

	function TreeViewItem(label) {

		this.element = document.createElement('div');

		this.label = document.createElement('label');
		this.label.textContent = label;
		this.element.appendChild(this.label);

		this.drawer = document.createElement('div');
		this.drawer.className = 'ngi-drawer';
		this.element.appendChild(this.drawer);

		this.caret = document.createElement('span');
		this.caret.className = 'ngi-caret';

		this.length = null;

		var collapsed = false;
		this.setCollapsed = function(newState) {
			if (collapsed = newState) {
				this.element.classList.add('ngi-collapsed');
				this.element.classList.remove('ngi-expanded');
			} else {
				this.element.classList.remove('ngi-collapsed');
				this.element.classList.add('ngi-expanded');
			}
		};
		this.toggle = function(e) {
			e.stopPropagation();
			this.setCollapsed(!collapsed);
		};
		this.caret.addEventListener('click', this.toggle.bind(this));

		var isCollapsible = false;
		this.makeCollapsible = function(collapsibleState, initialState) {
			if (isCollapsible == collapsibleState) {
				return;
			}
			if (isCollapsible = collapsibleState) {
				this.label.appendChild(this.caret);
				this.setCollapsed(initialState || false);
			} else {
				this.label.removeChild(this.caret);
			}
		}

		this.addChild = function(childItem, top) {
			if (!!top) {
				this.drawer.insertBefore(childItem.element, this.drawer.firstChild);
			} else {
				this.drawer.appendChild(childItem.element);
			}
		};

		this.removeChildren = function(className) {
			for (var i = this.drawer.childNodes.length - 1; i >= 0; i--) {
				var child = this.drawer.childNodes[i];
				if (child.classList.contains(className)) {
					this.drawer.removeChild(child);
				}
			}
		};

		this.destroy = function() {
			if (this.element.parentNode) {
				this.element.parentNode.removeChild(this.element);
			}
		};

		// Pill indicator
		var indicator = false;
		this.setIndicator = function(value) {
			if (indicator && typeof value !== 'number' && typeof value !== 'string') {
				indicator.parentNode.removeChild(indicator);
			} else {
				if (!indicator) {
					indicator = document.createElement('span');
					indicator.className = 'ngi-indicator';
					indicator.textContent = value;
					this.label.appendChild(indicator);
				}
			}
		};

		// Annotations (controller names, custom and built-in directive names)
		var annotations = [];
		this.addAnnotation = function(name, type) {
			if (annotations.indexOf(name) < 0) {
				annotations.push(name);
			} else {
				return;
			}
			var span = document.createElement('span');
			span.className = 'ngi-annotation';
			span.textContent = name;
			switch(type) {
				case NGI.Service.DIR:
					span.classList.add('ngi-annotation-dir');
					break;
				case NGI.Service.BUILTIN:
					span.classList.add('ngi-annotation-builtin');
					break;
				case NGI.Service.CTRL:
					span.classList.add('ngi-annotation-ctrl');
					break;
			}
			this.label.appendChild(span);
		};

		// Model types
		var type = null;
		this.setType = function(newType) {
			if (type) {
				this.element.classList.remove(type);
			}
			this.element.classList.add(newType);
			type = newType;
		};

	}

	function TreeView() {}

	// Creates a TreeViewItem instance, with styling and metadata relevant for
	// AngularJS apps
	TreeView.appItem = function(label, node) {
		if (node === document) node = document.querySelector('html');
		var item = new TreeViewItem(label);
		item.node = node;
		item.element.className = 'ngi-app';
		return item;
	};

	// Creates a TreeViewItem instance, with styling and metadata relevant for
	// AngularJS scopes
	TreeView.scopeItem = function(label, depth, isIsolate) {
		var item = new TreeViewItem(label);
		item.element.className = 'ngi-scope';
		item.makeCollapsible(true, false);
		if (isIsolate) {
			item.element.classList.add('ngi-isolate-scope');
		}
		item.label.className = 'ngi-depth-' + depth.length;

		// Highlight DOM elements the scope is attached to when hovering the item
		// in the inspector
		item.label.addEventListener('mouseover', function(event) {
			var isCaret = event.target && event.target.classList.contains('ngi-caret');
			if ( item.node && !window.ngInspector.pane.isResizing && !isCaret) {
				var target = (item.node === document) ?
					document.querySelector('html') : item.node;
				// target.classList.add('ngi-highlight');
				NGI.Highlighter.hl(target);
			}
		});
		item.label.addEventListener('mouseout', function() {
			if (item.node) {
				NGI.Highlighter.clear();
			}
		});

		// console.log the DOM Node this scope is attached to
		item.label.addEventListener('click', function() {
			console.log(item.node);
		});

		return item;
	};

	// Creates a TreeViewItem instance, with styling and metadata relevant for
	// AngularJS models
	TreeView.modelItem = function(key, value, depth) {
		var item = new TreeViewItem(key + ':');
		item.element.className = 'ngi-model';
		item.label.className = 'ngi-depth-' + depth.length;

		item.label.addEventListener('click', function() {
			console.log(value);
		});

		return item;
	};

	return TreeView;

})();
<|endoftext|>


<commit_before>define([
    'backbone',
    'computedProperty',
    'proxyProperty',
    'underscore'], function (Backbone, ComputedProperty, ProxyProperty, _) {

    return Backbone.Model.extend({
        unsettableProperties: [],

        set: function(key, val, options) {
            var self = this,
                attrs = {},
                stack = [],
                delayInitial = [],
                callSelf = false;

            this.computedCallbacks = this.computedCallbacks || {};
            if (key == null) {
                return this;
            } else if (typeof key == 'object') {
                attrs = key;
                options = val;

                _.each(key, function (attrValue, attrKey) {
                    if (attrValue instanceof ComputedProperty) {
                        if (!attrValue.skipInitialComputation) {
                            self.bindComputed(attrKey, attrValue);
                            callSelf = true;
                        } else {
                            delayInitial.push(function() {
                                self.bindComputed(attrKey, attrValue);
                            })
                        }
                        delete attrs[attrKey];
                    } else if (attrValue instanceof ProxyProperty) {
                        self.bindProxy(attrKey, attrValue);
                        delete attrs[attrKey];
                    } else {
                        if (self.computedCallbacks[attrKey]) {
                            stack.push(self.computedCallbacks[attrKey]);
                        }
                    }
                });
            } else {
                attrs[key] = val;
                if (val instanceof ComputedProperty) {
                    this.bindComputed(key, val);
                    return;
                } else if (val instanceof ProxyProperty) {
                    this.bindProxy(key, val);
                    return;
                } else {
                    if (this.computedCallbacks[key]) {
                        stack.push(this.computedCallbacks[key]);
                    }
                }
            }

            if (callSelf) {
                this.set.apply(this, [attrs, options]);
            } else {
                Backbone.Model.prototype.set.apply(this, [attrs, options]);
                _.forEach(stack, function(callbackArray) {
                    _.forEach(callbackArray, function(callback) {
                        callback.call(self);
                    });
                });
            }
            if (delayInitial) {
                _.forEach(delayInitial, function(cb) {
                    cb();
                });
            }
        },
        bindComputed: function (key, computed) {
            var self = this,
                callback;

            callback = function () {
                this.set(key, computed.callback.apply(this, this.getListenableValues(computed.listenables)));
            };

            _.forEach(computed.listenables, function(listenTo) {
                self.computedCallbacks[listenTo] = self.computedCallbacks[listenTo] || [];
                self.computedCallbacks[listenTo].push(callback);
                if (!computed.skipInitialComputation) {
                    callback.call(self);
                }
            });
        },
        bindProxy: function (key, proxy) {
            var self = this,
                model = proxy.model,
                modelAttribute = proxy.propertyNameOnModel;

            this.set(key, model.get(modelAttribute));

            model.on('change:' + modelAttribute, function() {
                self.set(key, model.get(modelAttribute));
            });

            this.on('change:' + key, function() {
                model.set(modelAttribute, self.get(key));
            });

        },
        getListenableValues: function (listenables) {
            var args = [],
                self = this;

            _.each(listenables, function (listenablePropertyKey) {
                args.push(self.get(listenablePropertyKey));
            });

            return args;
        }
    });
});

<commit_msg>Added toggleAttribute method, useful with rivets<commit_after>define([
    'backbone',
    'computedProperty',
    'proxyProperty',
    'underscore'], function (Backbone, ComputedProperty, ProxyProperty, _) {

    return Backbone.Model.extend({
        unsettableProperties: [],
        toggleAttribute: function(attribute) {
            this.set(attribute, !this.get(attribute));
        },
        set: function(key, val, options) {
            var self = this,
                attrs = {},
                stack = [],
                delayInitial = [],
                callSelf = false;

            this.computedCallbacks = this.computedCallbacks || {};
            if (key == null) {
                return this;
            } else if (typeof key == 'object') {
                attrs = key;
                options = val;

                _.each(key, function (attrValue, attrKey) {
                    if (attrValue instanceof ComputedProperty) {
                        if (!attrValue.skipInitialComputation) {
                            self.bindComputed(attrKey, attrValue);
                            callSelf = true;
                        } else {
                            delayInitial.push(function() {
                                self.bindComputed(attrKey, attrValue);
                            })
                        }
                        delete attrs[attrKey];
                    } else if (attrValue instanceof ProxyProperty) {
                        self.bindProxy(attrKey, attrValue);
                        delete attrs[attrKey];
                    } else {
                        if (self.computedCallbacks[attrKey]) {
                            stack.push(self.computedCallbacks[attrKey]);
                        }
                    }
                });
            } else {
                attrs[key] = val;
                if (val instanceof ComputedProperty) {
                    this.bindComputed(key, val);
                    return;
                } else if (val instanceof ProxyProperty) {
                    this.bindProxy(key, val);
                    return;
                } else {
                    if (this.computedCallbacks[key]) {
                        stack.push(this.computedCallbacks[key]);
                    }
                }
            }

            if (callSelf) {
                this.set.apply(this, [attrs, options]);
            } else {
                Backbone.Model.prototype.set.apply(this, [attrs, options]);
                _.forEach(stack, function(callbackArray) {
                    _.forEach(callbackArray, function(callback) {
                        callback.call(self);
                    });
                });
            }
            if (delayInitial) {
                _.forEach(delayInitial, function(cb) {
                    cb();
                });
            }
        },
        bindComputed: function (key, computed) {
            var self = this,
                callback;

            callback = function () {
                this.set(key, computed.callback.apply(this, this.getListenableValues(computed.listenables)));
            };

            _.forEach(computed.listenables, function(listenTo) {
                self.computedCallbacks[listenTo] = self.computedCallbacks[listenTo] || [];
                self.computedCallbacks[listenTo].push(callback);
                if (!computed.skipInitialComputation) {
                    callback.call(self);
                }
            });
        },
        bindProxy: function (key, proxy) {
            var self = this,
                model = proxy.model,
                modelAttribute = proxy.propertyNameOnModel;

            this.set(key, model.get(modelAttribute));

            model.on('change:' + modelAttribute, function() {
                self.set(key, model.get(modelAttribute));
            });

            this.on('change:' + key, function() {
                model.set(modelAttribute, self.get(key));
            });

        },
        getListenableValues: function (listenables) {
            var args = [],
                self = this;

            _.each(listenables, function (listenablePropertyKey) {
                args.push(self.get(listenablePropertyKey));
            });

            return args;
        }
    });
});

<|endoftext|>