<commit_before>// owf2.js
var owf = {
	render: function (ast) {
		var name = ast['name'], typeName = ast['type'];
		if(name == null)
			throw 'name cannot be null';
		if(typeName == null)
			throw name + 'needs a type';
		var type = owf.shapes[typeName];
		if(type == null)
			throw name + '\'s type -' + type + '- is unknown';
		var view = owf[type.kind].render(ast);
		if(type.isRoot)
			return view;
		else
			return owf.wrapView(view);
	},
	wrapView : function (view) {
		var wview = ''
			+ ''
			+ '	<title></title>'
			+ '	'
			+ ''
			+ view
			+ ''
			+ '';
		return wview;
	},
	widget: {
		// renders a widget by returning a string containing its view in html
		// expects the ast.type to map to a known type of shape
		// each shape type should have
		//   * a kind
		//   * slots, which are values into which instances can provide data.
		//     * if none are specified, a single slot called value is used by default.
		//   * a view which can be a string or a function
		//     * if its a string, render uses defaultView to interpolate values in the string using ${} notation.
		//     * if its a function, render expectes it to be of type fn(shape,ast) returns string. [TBD]
		//   * border: true: denotes a widget that needs a border to be drawn.
		//   * root: true/false: denotes that a widget is the root widget and doesnt need to be wrapped.

		// a widget instance should have a name, the type and slot nv pairs

		render: function (ast) {
			var shape = owf.shapes[ast.type];
			if(shape.kind != 'widget')
				throw 'widget.render renders only widgets.' + ast.type + 'has a different kind';
			var view = owf.widget.defaultView(shape,ast);
			return view;
		},
		defaultView: function (shape,ast) {
			if(shape.slots == null)
				shape.slots = {'value': ''};
			var view = shape.view;
			for(var s in shape.slots){
				var val = ast[s];
				if(val == undefined)
					val = shape.slots[s];
				if(val == undefined)
					val = '';
				view = view.replace('${' + s + '}', val);
			}
			// handle style now
			var style = '';
			if(shape.style != undefined){
				style = 'style="'+shape.style+'"'
			}
			style = 'class="'+ (shape.hasBorder? 'lofibox' : '')  +'" ' + style;
			view = view.replace('${style}', style);
			return view;
		}
	},
	container: {
		// renders a container, which is defined by
		// * a kind
		// * a view for the container itself
		// * contents array of views for each slot in its contents.

		render: function (ast) {
			var shape = owf.shapes[ast.type];
			if(shape.kind != 'container')
				throw 'container.render renders only containers.' + ast.type + 'has a different kind';
			if(shape.contents == null)
				throw 'container ' + ast.type + 'doesnt have any contents. needs redefinition'
			if(ast.has == null)
				throw ast.name + ' needs a "has" attribute to use ' + ast.type
			if(ast.has.length != shape.contents.length)
				throw ast.name + ' has ' + ast.has.length + ' contents, needs ' +shape.contents.length+ 'to use ' + ast.type

			var view = owf.container.defaultView(shape,ast);
			return view;
		},
		defaultView: function (shape,ast) {
			var childrenView = '';
			for(var c in shape.contents){
				var child = ast.has[c];
				var childView = owf.render(child);
				childrenView += owf.container.defaultChildView(shape.contents[c], childView);
			}
			var view = shape.view.replace('${content}',childrenView);
			// handle style now
			var style = '';
			if(shape.style != undefined){
				style = 'style="'+shape.style+'"'
			}
			view = view.replace('${style}', style);
			return view;
		},
		defaultChildView: function (childShape,childView) {
			return childShape.replace('${value}', childView);
		}
	},
	state: {
		render: function (ast) {

		}
	},
	app: {
		render: function (ast) {

		}
	},
	shapes: {
		text: {
			kind: 'widget',
			view: "${value}",
			slots: {value: 'Lorem Ipsum'},
			style: 'font-weight:bold'
		},
		title: {
			kind: 'widget',
			view: "
${value}
",
			slots: {value: 'Lorem Ipsum'},
			style: ''
		},
		textbox: {
			kind: 'widget',
			view: "<input ${style} type=text value='${value}'></input>",
			slots: {value: 'Lorem Ipsum'},
			style: '',
			hasBorder: true
		},
		textarea: {
			kind: 'widget',
			view: "<textarea ${style}>${value}</textarea>",
			slots: {value: 'Lorem Ipsum'},
			style: '',
			hasBorder: true
		},
		button: {
			kind: 'widget',
			view: '<input ${style} type=button value="${value}"/>',
			slots: {value: 'Ok'},
			hasBorder: true
		},
		twocolumns: {
			kind: 'container',
			view: '
${content}
',
			contents: ['
${value}
','
${value}
'],
		},
		threecolumns: {
			kind: 'container',
			view: '
${content}
',
			contents: ['
${value}
','
${value}
','
${value}
'],
		},
		tworows: {
			kind: 'container',
			view: '
${content}
',
			contents: ['
${value}
','
${value}
'],
		},
	}
}
<commit_msg>consolidated rules in one place and added support for width and height attrs<commit_after>// owf2.js
var owf = {
	render: function (ast) {
		var name = ast['name'], typeName = ast['type'];
		if(name == null)
			throw 'name cannot be null';
		if(typeName == null)
			throw name + 'needs a type';
		var type = owf.shapes[typeName];
		if(type == null)
			throw name + '\'s type -' + type + '- is unknown';
		var view = owf[type.kind].render(ast);
		if(type.isRoot)
			return view;
		else
			return owf.wrapView(view);
	},
	wrapView : function (view) {
		var wview = ''
			+ ''
			+ '	<title></title>'
			+ '	'
			+ ''
			+ view
			+ ''
			+ '';
		return wview;
	},
	widget: {
		// renders a widget by returning a string containing its view in html
		// expects the ast.type to map to a known type of shape
		// see below for rules

		// a widget instance should have a name, the type and slot nv pairs

		render: function (ast) {
			var shape = owf.shapes[ast.type];
			if(shape.kind != 'widget')
				throw 'widget.render renders only widgets.' + ast.type + 'has a different kind';
			var view = owf.widget.defaultView(shape,ast);
			return view;
		},
		defaultView: function (shape,ast) {
			if(shape.slots == null)
				shape.slots = {'value': ''};
			var view = shape.view;
			for(var s in shape.slots){
				if(s == 'width' || s == 'height')
					continue;
				var val = ast[s];
				if(val == undefined)
					val = shape.slots[s];
				if(val == undefined)
					val = '';
				view = view.replace('${' + s + '}', val);
			}
			// handle style now
			var style = 'style="' + ((shape.style != undefined && shape.style != '') ? shape.style + ';' : '');
			var width = ast['width'];
			if(width && (width != undefined || width != ''))
				width = '100%';
			var height = ast['height'];
			if(height && (height != undefined || height != ''))
				height = '100%';

			style += 'width:'+width+';height:'+height+';';
			style += '"'
			style = 'class="'+ (shape.hasBorder? 'lofibox' : '')  +'" ' + style;
			view = view.replace('${style}', style);
			return view;
		}
	},
	container: {
		// renders a container, which is defined by
		// * a kind
		// * a view for the container itself
		// * contents array of views for each slot in its contents.

		render: function (ast) {
			var shape = owf.shapes[ast.type];
			if(shape.kind != 'container')
				throw 'container.render renders only containers.' + ast.type + 'has a different kind';
			if(shape.contents == null)
				throw 'container ' + ast.type + 'doesnt have any contents. needs redefinition'
			if(ast.has == null)
				throw ast.name + ' needs a "has" attribute to use ' + ast.type
			if(ast.has.length != shape.contents.length)
				throw ast.name + ' has ' + ast.has.length + ' contents, needs ' +shape.contents.length+ 'to use ' + ast.type

			var view = owf.container.defaultView(shape,ast);
			return view;
		},
		defaultView: function (shape,ast) {
			var childrenView = '';
			for(var c in shape.contents){
				var child = ast.has[c];
				var childView = owf.render(child);
				childrenView += owf.container.defaultChildView(shape.contents[c], childView);
			}
			var view = shape.view.replace('${content}',childrenView);
			// handle style now
			var style = '';
			if(shape.style != undefined){
				style = 'style="'+shape.style+'"'
			}
			view = view.replace('${style}', style);
			return view;
		},
		defaultChildView: function (childShape,childView) {
			return childShape.replace('${value}', childView);
		}
	},
	state: {
		render: function (ast) {

		}
	},
	app: {
		render: function (ast) {

		}
	},
	// rules for shapes:
	// kind: required. One of widget, container, state or app
	// slots: optional.array of name-value pairs. Names are used to map from the instance. Values are default values
	// 		Default: if there's only one value, skip defining it and owf will use a single slot called 'value'
	// view: required.string or function.
	// 		If string, should have ${} delimited names from slots for replacement.
	//			In addition, the string should have a '${shape}' string in the appropriate place
	// 			that stands for *both* the class attribute and the style attribute of the generated element.
	// 			owf replaces this string with: `class = "..." style = "..."`
	// 		if function, should have signature str=fn(shape,ast) where
	//			str = returned string containing html view in a div
	//			shape = shape definition passed in by owf
	//			ast = part of the instance's ast parsed by owf and passed into the function.
	// style: optional. css styles appled to the instance's view. Default: empty string.
	// width and height: optional. dimensions supplied separately from slots in the shape definition,
	// 					but supplied along with other slot values in the instance. owf will take these values
	// 					and add them to the style attribute. html width and height attributes are not used.
	// 		Default: 100% or 99% for all elements.
	// border: optional. true denotes a widget that needs a border to be drawn. owf will then draw a lo-fi border
	//		Default: false
	// root: optional. true denotes that a widget is the root widget and doesnt need to be wrapped in std markup
	// 		only shapes of kind 'app' are expected to have this field and their view function is expected to handle:
	// 			- overall html markup
	// 			- setting up the global css styles for boxes and fonts
	//		Default: false
	shapes: {
		text: {
			kind: 'widget',
			view: "${value}",
			slots: {value: 'Lorem Ipsum'},
			style: 'font-weight:bold'
		},
		title: {
			kind: 'widget',
			view: "
${value}
",
			slots: {value: 'Lorem Ipsum'},
			style: ''
		},
		textbox: {
			kind: 'widget',
			view: "<input ${style} type=text value='${value}'></input>",
			slots: {value: 'Lorem Ipsum'},
			style: '',
			hasBorder: true
		},
		textarea: {
			kind: 'widget',
			view: "<textarea ${style}>${value}</textarea>",
			slots: {value: 'Lorem Ipsum'},
			style: '',
			hasBorder: true
		},
		button: {
			kind: 'widget',
			view: '<input ${style} type=button value="${value}"/>',
			slots: {value: 'Ok'},
			hasBorder: true
		},
		twocolumns: {
			kind: 'container',
			view: '
${content}
',
			contents: ['
${value}
','
${value}
'],
		},
		threecolumns: {
			kind: 'container',
			view: '
${content}
',
			contents: ['
${value}
','
${value}
','
${value}
'],
		},
		tworows: {
			kind: 'container',
			view: '
${content}
',
			contents: ['
${value}
','
${value}
'],
		},
	}
}
<|endoftext|>


<commit_before>/*
Copyright (c) 2012 Rüdiger Appel
Copyright (c) 2015 Roger Meier <roger@bufferoverflow.ch>
Copyright (c) 2015 Gerard Braad <me@gbraad.nl>

SPDX-License-Identifier:	MIT
*/

document.addEventListener ('DOMContentLoaded', function() {
	loadSvg("clockface", "/assets/clockface.svg");
	setInterval(tick, 50);
}, false);

var loadSvg = function(target, filename) {
	var xhr = new XMLHttpRequest();
        xhr.open("GET", filename, false);
	xhr.send();
	svgDoc = xhr.responseText;
	document.getElementById(target).innerHTML = svgDoc;
}

var tick = function() {
	var now     = new Date();
  var hours   = now.getHours();
  var minutes = now.getMinutes();
  var time    = Math.min(60000, 1.025 * (1000 * now.getSeconds() + now.getMilliseconds()));
  var seconds = Math.floor(time / 1000);
  var millis  = time % 1000;
  rotate('hourHand',   hours * 30 + minutes * 0.5);
  rotate('minuteHand', minutes * 6);
  rotate('secondHand', 6 * seconds + 3 * (1 + Math.cos(Math.PI + Math.PI * (0.001 * millis))));
};

function rotate(id, angle) {
  var element = document.getElementById(id);
  if (element) {
    element.setAttribute('transform', 'rotate(' + angle + ', 100, 100)');
    if (element.getAttribute('visibility') == 'hidden') {
      element.setAttribute('visibility', 'visible');
    }
  }
}
<commit_msg>Remove unnecessary code<commit_after>/*
Copyright (c) 2012 Rüdiger Appel
Copyright (c) 2015 Roger Meier <roger@bufferoverflow.ch>
Copyright (c) 2015 Gerard Braad <me@gbraad.nl>

SPDX-License-Identifier:	MIT
*/

document.addEventListener ('DOMContentLoaded', function() {
	loadSvg("clockface", "/assets/clockface.svg");
	setInterval(tick, 50);
}, false);

var loadSvg = function(target, filename) {
	var xhr = new XMLHttpRequest();
        xhr.open("GET", filename, false);
	xhr.send();
	svgDoc = xhr.responseText;
	document.getElementById(target).innerHTML = svgDoc;
}

var tick = function() {
	var now     = new Date();
	var hours   = now.getHours();
	var minutes = now.getMinutes();
	var time    = Math.min(60000, 1.025 * (1000 * now.getSeconds() + now.getMilliseconds()));
	var seconds = Math.floor(time / 1000);
	var millis  = time % 1000;
	rotate('hourHand',   hours * 30 + minutes * 0.5);
	rotate('minuteHand', minutes * 6);
	rotate('secondHand', 6 * seconds + 3 * (1 + Math.cos(Math.PI + Math.PI * (0.001 * millis))));
};

function rotate(id, angle) {
	var element = document.getElementById(id);
	if (element) {
		element.setAttribute('transform', 'rotate(' + angle + ', 100, 100)');
	}
}
<|endoftext|>


<commit_before>const { api, _, Action } = require("../..")
const path = require('path')
const traverse = require("traverse")
const mkdirp = require('mkdirp')
const Abortable = require('pull-abortable')
const pl = require('pull-level')
const leveldown = require('leveldown')
const encode = require('encoding-down')
const Intervals = require("../../lib/intervals")
const util = require('icebreaker-network/lib/util')

const filter = require('pull-async-filter')

module.exports = () => {
  const dir = path.join(api.config.path, "addrs")
  try { mkdirp.sync(dir) }
  catch (err) { }

  const db = require('levelup')(encode(leveldown(dir), { valueEncoding: 'json' }))

  const timers = new Intervals()

  const add = (data, cb) =>{
    const r = Object.assign({},data)
    return db.get(r.key, (err, d) => {
    if(err)return db.put(r.key, r, (err)=> cb(err,r) )
      if(d.ts && data.ts>d.ts) return db.put(r.key, r, (err)=>cb(err,r))
      return      cb(null, d)
    })
  }

  const ls = {}

  const write =  () => {
    return _.asyncMap((item, cb) => {
      if(item && item.ts && item.key)
      add(item, (err) => cb(err, item) )
      return  cb(null, item)
    })
  }

  api.addrs = api.actions.addrs = Action({
    type: "source",
    input: { live: "boolean|string", old: "boolean|string" },
    desc: "Returns all known addresses",
    usage: {
      live: "Receive all addresses until cancelled",
      old: "Recive all old addresses"
    },
    defaults: {
      live: false,
      old: true
    },
    run: function(opts) {

      opts.old = opts.old === "true" || opts.old === true ? true : false
      opts.live = opts.live === "true" || opts.live === true ? true : false
      opts.keys = true
      opts.sync = false

      const peerID = this.id

      return _(pl.read(db, opts),_.filter((item)=>{

        if(!item)return false
        if(item.type!=="put" && item.type)return false
        if(!item.value)return false
        if(!item.value.ts){
          db.del(item.key,function(err){})
          return false
        }
        const ts = Date.now() - api.config.connectionTimeout
        if(item.value.ts<ts == true){
            db.del(item.key,function(err){})
          return false
        }
        delete item.type
        return item.value.ts>ts
      }),_.map((item)=>Object.assign({},item.value)),_.asyncMap((item,cb)=>{
        if(!peerID) return     cb(null,item)

        api.friends.isFriend(peerID,function(err,isFriend){
          if(err) return cb(null,undefined);
          if(isFriend) return  cb(null,item)

        api.identities.get(peerID,(err,identity)=>{
          if(err) return cb(null,undefined);

          const  u = util.parseUrl(item.key)
          api.identities.get(u.auth,(err,id)=>{
            if(err) return cb(null,undefined)
            for(g of identity.groups){
              if(id.groups.indexOf(g) !== -1)
              {
                api.groups.get(g,function(err,group){
                  if(err) return cb(null,undefined)
                  if(group.allow.indexOf("*")!==-1||group.allow.indexOf(u.pathname.split("/").slice(2).join(".") !==-1))
                  return cb(null,item)
                  return cb(null,undefined)

                })

              }
            }
            return cb(null,undefined);
          })

        });
      })

      }),_.filter()
      )
    }
  })

  const addAddrs = (e,map) => {
    const ts = Date.now()
    if (e.addrs && e.peer)
    e.addrs.forEach(addr=> {
      traverse(e.peer).forEach(function () {
        if (_.isFunction(this.node)) {
          let value = Object.assign({ key: addr + "/" + this.path.join("/"), ts: ts,action:this.path.join(".") },this.node)
          if(map) value =map(value)
          if(value.ts)
            add(value, (err) => {
              if (err) return api.log.error(err)
            })
          }
        })
      })
  }

  const addClient = (e) => {
    _(api.addrs({old:true,live:false}),
    filter((item,cb) => {
      if(item.key.indexOf("://"+api.id+"@") === -1) return cb(null,true)
        const u =util.parseUrl(item.key)
        const path = u.pathname
        if(path && !path.startsWith("/"+api.config.appKey+"/protoNames")) return cb(null,true)
        api.friends.isFriend(u.auth, (err,isFriend)=> {
          return cb(null,!(isFriend && path && path.startsWith("/"+api.config.appKey+"/protoNames") && !u.query.gw))
        })
      }
      ),
    _.collect((err,addrs)=>{
      if(err) return
      if(!addrs) return
      if(addrs.length === 0) return;
      let maxts
      addrs.forEach((item)=>{ maxts = Math.max(maxts||0,item.ts) })
      addrs=addrs.filter((addr)=>{ return addr.ts === maxts })
      addrs = addrs.map((addr)=>{
        return addr.key.replace("/protoNames","")
      })
      addAddrs({addrs:addrs.slice(0).map((addr)=>e.remoteAddress),peer:e.peer},function(data){
        data.key = data.key+"?gw="+this.addrs.shift();
        return data
      }.bind({addrs:addrs.slice(0)}))
    }))
  }

  _(api.events(), api.events.on({
    connection:(e)=>{
      if(e.remoteAddress)
      api.friends.isFriend(e.peerID,(err,isFriend)=>{

        if(!isFriend &&  e.peer && e.peerID !== api.id){
          timers.start(e.id, ()=>addClient(e), api.config.pingInterval)
           addClient(e)
         return
        }

        if(isFriend && e.peer && !e.peer.protoNames && e.peerID !== api.id)
        {
              timers.start(e.id, ()=>addClient(e), api.config.pingInterval)
              addClient(e)
              return

        }

        if(isFriend && e.peer && e.peer.protoNames && e.peerID !== api.id)
        {
            e.peer.protoNames(function(err,protos){
              if(err)return;
              if(protos.length===0){

                timers.start(e.id, ()=>addClient(e), api.config.pingInterval)
                addClient(e)
              }

            })
        }
      })

    },
    closer: (e) => {
      try {
        if(e.address){
          addAddrs(e)
          timers.start(e.id,()=>addAddrs(e),api.config.pingInterval)
        }
        if (!e.peer.addrs) return
        if (!ls[e.id]) {
          const done = () => {
            if (ls[e.id]) {
              ls[e.id].abort(true)
              delete ls[e.id]
              api.log.info('Stop listening to live address changes from', e.peerID, "on", api.id)
            }
          }

          ls[e.id] = Abortable(done)

          api.log.info('Listen to live address changes from', e.peerID, "on", api.id)
          _(e.peer.addrs({ old: true, live: true }), ls[e.id], write(), _.onEnd(done))
        }
      }
      catch (err) {
        api.log.error("Addresses api error", err.message || err)
      }

    },
    notcloser: (e) => {
      if(!e.remoteAddress)addAddrs(e)

      timers.stop(e.id)
      if (ls[e.id]) {
        ls[e.id].abort(true)
        delete ls[e.id]
      }
    },
    replicate: (e) => {
      try {
        api.log('Replicating addresses from peer', e.peerID)

        _(e.peer.addrs({ old: true, live: false }), write(), _.onEnd((err) => {
          if (err) api.log.error("Addresses replication error", err.message || err)
        }))

        api.log('Addresses from', e.peerID, 'replicated')
      }
      catch (err) {
        api.log.error("Addresses replication error", err.message || err)
      }
    },
    disconnection:(e)=>{
      timers.stop(e.id)
    },
    end: (e) => {

    }

  }))

  const events = _.events()
  const end = events.end
  events.end=()=>{
    timers.stopAll()
    db.close(function(){
      end()
    })
  }

  db.once("ready",function(){
    events.emit({type:"ready"})
  })

  return events

}
<commit_msg>add console.log<commit_after>const { api, _, Action } = require("../..")
const path = require('path')
const traverse = require("traverse")
const mkdirp = require('mkdirp')
const Abortable = require('pull-abortable')
const pl = require('pull-level')
const leveldown = require('leveldown')
const encode = require('encoding-down')
const Intervals = require("../../lib/intervals")
const util = require('icebreaker-network/lib/util')

const filter = require('pull-async-filter')

module.exports = () => {
  const dir = path.join(api.config.path, "addrs")
  try { mkdirp.sync(dir) }
  catch (err) { }

  const db = require('levelup')(encode(leveldown(dir), { valueEncoding: 'json' }))

  const timers = new Intervals()

  const add = (data, cb) =>{
    const r = Object.assign({},data)
    return db.get(r.key, (err, d) => {
    if(err)return db.put(r.key, r, (err)=> cb(err,r) )
      if(d.ts && data.ts>d.ts) return db.put(r.key, r, (err)=>cb(err,r))
      return      cb(null, d)
    })
  }

  const ls = {}

  const write =  () => {
    return _.asyncMap((item, cb) => {
      if(item && item.ts && item.key)
      add(item, (err) => cb(err, item) )
      return  cb(null, item)
    })
  }

  api.addrs = api.actions.addrs = Action({
    type: "source",
    input: { live: "boolean|string", old: "boolean|string" },
    desc: "Returns all known addresses",
    usage: {
      live: "Receive all addresses until cancelled",
      old: "Recive all old addresses"
    },
    defaults: {
      live: false,
      old: true
    },
    run: function(opts) {

      opts.old = opts.old === "true" || opts.old === true ? true : false
      opts.live = opts.live === "true" || opts.live === true ? true : false
      opts.keys = true
      opts.sync = false

      const peerID = this.id

      return _(pl.read(db, opts),_.filter((item)=>{

        if(!item)return false
        if(item.type!=="put" && item.type)return false
        if(!item.value)return false
        if(!item.value.ts){
          db.del(item.key,function(err){})
          return false
        }
        const ts = Date.now() - api.config.connectionTimeout
        if(item.value.ts<ts == true){
            db.del(item.key,function(err){})
          return false
        }
        delete item.type
        return item.value.ts>ts
      }),_.map((item)=>Object.assign({},item.value)),_.asyncMap((item,cb)=>{
        if(!peerID) return     cb(null,item)

        api.friends.isFriend(peerID,function(err,isFriend){
          if(err) return cb(null,undefined);
          if(isFriend) return  cb(null,item)

        api.identities.get(peerID,(err,identity)=>{
          if(err) return cb(null,undefined);

          const  u = util.parseUrl(item.key)
          api.identities.get(u.auth,(err,id)=>{
            if(err) return cb(null,undefined)
            for(g of identity.groups){
              if(id.groups.indexOf(g) !== -1)
              {
                api.groups.get(g,function(err,group){
                  if(err) return cb(null,undefined)
                  if(group.allow.indexOf("*")!==-1||group.allow.indexOf(u.pathname.split("/").slice(2).join(".") !==-1))
                  return cb(null,item)
                  return cb(null,undefined)

                })

              }
            }
            return cb(null,undefined);
          })

        });
      })

      }),_.filter()
      )
    }
  })

  const addAddrs = (e,map) => {
    const ts = Date.now()
    if (e.addrs && e.peer)
    e.addrs.forEach(addr=> {
      traverse(e.peer).forEach(function () {
        if (_.isFunction(this.node)) {
          let value = Object.assign({ key: addr + "/" + this.path.join("/"), ts: ts,action:this.path.join(".") },this.node)
          if(map) value =map(value)
          if(value.ts)
            add(value, (err) => {
              if (err) return api.log.error(err)
            })
          }
        })
      })
  }

  const addClient = (e) => {
    _(api.addrs({old:true,live:false}),
    filter((item,cb) => {
      if(item.key.indexOf("://"+api.id+"@") === -1) return cb(null,true)
        const u =util.parseUrl(item.key)
        const path = u.pathname
        if(path && !path.startsWith("/"+api.config.appKey+"/protoNames")) return cb(null,true)
        api.friends.isFriend(u.auth, (err,isFriend)=> {
          return cb(null,!(isFriend && path && path.startsWith("/"+api.config.appKey+"/protoNames") && !u.query.gw))
        })
      }
      ),
    _.collect((err,addrs)=>{
      console.log("addrs",addrs)
      if(err) return
      if(!addrs) return
      if(addrs.length === 0) return;
      let maxts
      addrs.forEach((item)=>{ maxts = Math.max(maxts||0,item.ts) })
      addrs=addrs.filter((addr)=>{ return addr.ts === maxts })
      addrs = addrs.map((addr)=>{
        return addr.key.replace("/protoNames","")
      })
      addAddrs({addrs:addrs.slice(0).map((addr)=>e.remoteAddress),peer:e.peer},function(data){
        console.log("addAddrs",this.addrs)
        data.key = data.key+"?gw="+this.addrs.shift();
        return data
      }.bind({addrs:addrs.slice(0)}))
    }))
  }

  _(api.events(), api.events.on({
    connection:(e)=>{
      if(e.remoteAddress)
      api.friends.isFriend(e.peerID,(err,isFriend)=>{

        if(!isFriend &&  e.peer && e.peerID !== api.id){
          timers.start(e.id, ()=>addClient(e), api.config.pingInterval)
           addClient(e)
         return
        }

        if(isFriend && e.peer && !e.peer.protoNames && e.peerID !== api.id)
        {
              timers.start(e.id, ()=>addClient(e), api.config.pingInterval)
              addClient(e)
              return

        }

        if(isFriend && e.peer && e.peer.protoNames && e.peerID !== api.id)
        {
            e.peer.protoNames(function(err,protos){
              if(err)return;
              if(protos.length===0){

                timers.start(e.id, ()=>addClient(e), api.config.pingInterval)
                addClient(e)
              }

            })
        }
      })

    },
    closer: (e) => {
      try {
        if(e.address){
          addAddrs(e)
          timers.start(e.id,()=>addAddrs(e),api.config.pingInterval)
        }
        if (!e.peer.addrs) return
        if (!ls[e.id]) {
          const done = () => {
            if (ls[e.id]) {
              ls[e.id].abort(true)
              delete ls[e.id]
              api.log.info('Stop listening to live address changes from', e.peerID, "on", api.id)
            }
          }

          ls[e.id] = Abortable(done)

          api.log.info('Listen to live address changes from', e.peerID, "on", api.id)
          _(e.peer.addrs({ old: true, live: true }), ls[e.id], write(), _.onEnd(done))
        }
      }
      catch (err) {
        api.log.error("Addresses api error", err.message || err)
      }

    },
    notcloser: (e) => {
      if(!e.remoteAddress)addAddrs(e)

      timers.stop(e.id)
      if (ls[e.id]) {
        ls[e.id].abort(true)
        delete ls[e.id]
      }
    },
    replicate: (e) => {
      try {
        api.log('Replicating addresses from peer', e.peerID)

        _(e.peer.addrs({ old: true, live: false }), write(), _.onEnd((err) => {
          if (err) api.log.error("Addresses replication error", err.message || err)
        }))

        api.log('Addresses from', e.peerID, 'replicated')
      }
      catch (err) {
        api.log.error("Addresses replication error", err.message || err)
      }
    },
    disconnection:(e)=>{
      timers.stop(e.id)
    },
    end: (e) => {

    }

  }))

  const events = _.events()
  const end = events.end
  events.end=()=>{
    timers.stopAll()
    db.close(function(){
      end()
    })
  }

  db.once("ready",function(){
    events.emit({type:"ready"})
  })

  return events

}
<|endoftext|>


<commit_before>/* =============================================================

    Astro v3.2
    Mobile-first navigation patterns by Chris Ferdinandi.
    http://gomakethings.com

    Free to use under the MIT License.
    http://gomakethings.com/mit/

 * ============================================================= */

/* =============================================================
    MICRO-FRAMEWORK
    Simple vanilla JavaScript functions to handle common tasks.
 * ============================================================= */

// Check if an element has a class
var hasClass = function (elem, className) {
    return new RegExp(' ' + className + ' ').test(' ' + elem.className + ' ');
}

// Add a class to an element
var addClass = function (elem, className) {
    if (!hasClass(elem, className)) {
        elem.className += ' ' + className;
    }
}

// Remove a class from an element
var removeClass = function (elem, className) {
    var newClass = ' ' + elem.className.replace( /[\t\r\n]/g, ' ') + ' ';
    if (hasClass(elem, className)) {
        while (newClass.indexOf(' ' + className + ' ') >= 0 ) {
            newClass = newClass.replace(' ' + className + ' ', ' ');
        }
        elem.className = newClass.replace(/^\s+|\s+$/g, '');
    }
}

// Toggle a class on an element
var toggleClass = function (elem, className) {
    if ( hasClass(elem, className) ) {
        removeClass(elem, className);
    }
    else {
        addClass(elem, className);
    }
}

/* =============================================================
    ASTRO FUNCTIONS
    Toggle the navigation menu.
 * ============================================================= */

// Feature Test
if ( 'querySelector' in document && 'addEventListener' in window ) {

    // Function to toggle navigation menu
    var toggleNav = function (toggle) {

        // Get target navigation menu
        var dataID = toggle.getAttribute('data-target');
        var dataTarget = document.querySelector(dataID);

        // Toggle the '.active' class on the menu
        toggleClass(dataTarget, 'active');

    }

    // Define the nav toggle
    var navToggle = document.querySelectorAll('.nav-toggle');

    // For each nav toggle
    [].forEach.call(navToggle, function (toggle) {

        // When nav toggle is clicked
        toggle.addEventListener('click', function(e) {

            // Prevent the default link behavior
            e.preventDefault();

            // Toggle the navigation menu
            toggleNav(toggle);

        }, false);
    });
}
<commit_msg>Updated feature test<commit_after>/* =============================================================

    Astro v3.2
    Mobile-first navigation patterns by Chris Ferdinandi.
    http://gomakethings.com

    Free to use under the MIT License.
    http://gomakethings.com/mit/

 * ============================================================= */

/* =============================================================
    MICRO-FRAMEWORK
    Simple vanilla JavaScript functions to handle common tasks.
 * ============================================================= */

// Check if an element has a class
var hasClass = function (elem, className) {
    return new RegExp(' ' + className + ' ').test(' ' + elem.className + ' ');
}

// Add a class to an element
var addClass = function (elem, className) {
    if (!hasClass(elem, className)) {
        elem.className += ' ' + className;
    }
}

// Remove a class from an element
var removeClass = function (elem, className) {
    var newClass = ' ' + elem.className.replace( /[\t\r\n]/g, ' ') + ' ';
    if (hasClass(elem, className)) {
        while (newClass.indexOf(' ' + className + ' ') >= 0 ) {
            newClass = newClass.replace(' ' + className + ' ', ' ');
        }
        elem.className = newClass.replace(/^\s+|\s+$/g, '');
    }
}

// Toggle a class on an element
var toggleClass = function (elem, className) {
    if ( hasClass(elem, className) ) {
        removeClass(elem, className);
    }
    else {
        addClass(elem, className);
    }
}

/* =============================================================
    ASTRO FUNCTIONS
    Toggle the navigation menu.
 * ============================================================= */

// Feature Test
if ( 'querySelector' in document && 'addEventListener' in window && Array.prototype.forEach ) {

    // Function to toggle navigation menu
    var toggleNav = function (toggle) {

        // Get target navigation menu
        var dataID = toggle.getAttribute('data-target');
        var dataTarget = document.querySelector(dataID);

        // Toggle the '.active' class on the menu
        toggleClass(dataTarget, 'active');

    }

    // Define the nav toggle
    var navToggle = document.querySelectorAll('.nav-toggle');

    // For each nav toggle
    [].forEach.call(navToggle, function (toggle) {

        // When nav toggle is clicked
        toggle.addEventListener('click', function(e) {

            // Prevent the default link behavior
            e.preventDefault();

            // Toggle the navigation menu
            toggleNav(toggle);

        }, false);
    });
}
<|endoftext|>


<commit_before>$(function() {

    Endeavour.Model.Abstract = Backbone.Model.extend({

        idAttribute: 'ID',

        fetch: function(options) {
            options = options ? _.clone(options) : {};
            options.beforeSend = Endeavour.ajaxSetHeaders;
            console.log('fetching', options);
            return Backbone.Model.prototype.fetch.call(this, options);
        },

    });

    Endeavour.Collection.Abstract = Backbone.Collection.extend({

        fetch: function(options) {
            options = options ? _.clone(options) : {};
            options.beforeSend = Endeavour.ajaxSetHeaders;
            console.log('fetching', options);
            return Backbone.Collection.prototype.fetch.call(this, options);
        },

    });

});<commit_msg>Abstract Model & Collections update - Abstract Model now adds options.ajaxSetHeaders on Model.sync() instead of just Model.fetch() - Abstract Collection now adds options.ajaxSetHeaders on Collection.sync() instead of just Collection.fetch()<commit_after>$(function() {

    Endeavour.Model.Abstract = Backbone.Model.extend({

        idAttribute: 'ID',

        sync: function(method, model, options) {
            options.beforeSend = Endeavour.ajaxSetHeaders;
            return Backbone.Model.prototype.sync.call(this, method, model, options);
        },

    });

    Endeavour.Collection.Abstract = Backbone.Collection.extend({

        sync: function(method, model, options) {
            options.beforeSend = Endeavour.ajaxSetHeaders;
            return Backbone.Collection.prototype.sync.call(this, method, model, options);
        },

    });

});<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013, Yahoo! Inc.  All rights reserved.
 * Copyrights licensed under the New BSD License.
 * See the accompanying LICENSE file for terms.
 */

/*jslint node: true, nomen: true */

'use strict';

var View = require('./lib/view'),
    debug = require('debug')('express:view');

function expressView(app) {
    // storing a reference to the original view class
    // implementation so we can fallback to that if
    // there is not a synthetic template available
    var OrignalViewClass = app.get('view');

    if (app['@view']) { return app; }

    // Brand.
    Object.defineProperty(app, '@view', {
        value: expressView
    });

    function ViewShim(name, options) {
        var v, o;

        if (!app.get('locator')) {
            debug('Call `app.set("locator", locatorObj)` before extending the `express` app with `express-lang`');
            throw new Error('Locator instance should be mounted');
        }

        options = options || {};
        options.locator = app.get('locator');
        options.layout = app.get('layout');

        v = new View(name, options);
        // piping the important pieces to shim the view instance
        this.name = v.name;
        this.path = v.path;
        this.render = function (opts, fn) {
            // trying to render with the view implementation that relies on
            // synthetic views from locator. this render process is sync,
            // that's why we have a giant try/catch here.
            try {
                v.render(opts, function (err) {
                    if (err) {
                        debug('Failed to lookup synthetic view "%s" thru `locator`', name);
                        // fallback to the original implementation (which might be cacheable)
                        // TODO: the following line might throw, what should we do?
                        if (!o || !opts.cache) {
                            o = new OrignalViewClass(name, options);
                        }
                        if (!o.path) {
                            throw new Error('Failed to lookup view "' + name + '"');
                        }
                        // render using the original implemented from express which
                        // is going to try to use the filesystem and the view engines
                        o.render(opts, fn);
                        return;
                    }
                    fn.apply(this, arguments);
                });
            } catch (err) {
                err.view = v;
                fn(err);
            }
        };
    }

    // Modifies the Express `app`.
    app.set('view', ViewShim);

    return app;
}

exports.extend = expressView;
<commit_msg>Fix log message since setting the locator object is no longer required prior to extending the app.<commit_after>/*
 * Copyright (c) 2013, Yahoo! Inc.  All rights reserved.
 * Copyrights licensed under the New BSD License.
 * See the accompanying LICENSE file for terms.
 */

/*jslint node: true, nomen: true */

'use strict';

var View = require('./lib/view'),
    debug = require('debug')('express:view');

function expressView(app) {
    // storing a reference to the original view class
    // implementation so we can fallback to that if
    // there is not a synthetic template available
    var OrignalViewClass = app.get('view');

    if (app['@view']) { return app; }

    // Brand.
    Object.defineProperty(app, '@view', {
        value: expressView
    });

    function ViewShim(name, options) {
        var v, o;

        if (!app.get('locator')) {
            debug('Call `app.set("locator", locatorObj)` before listening for traffic on the `express` app.');
            throw new Error('Locator instance should be mounted');
        }

        options = options || {};
        options.locator = app.get('locator');
        options.layout = app.get('layout');

        v = new View(name, options);
        // piping the important pieces to shim the view instance
        this.name = v.name;
        this.path = v.path;
        this.render = function (opts, fn) {
            // trying to render with the view implementation that relies on
            // synthetic views from locator. this render process is sync,
            // that's why we have a giant try/catch here.
            try {
                v.render(opts, function (err) {
                    if (err) {
                        debug('Failed to lookup synthetic view "%s" thru `locator`', name);
                        // fallback to the original implementation (which might be cacheable)
                        // TODO: the following line might throw, what should we do?
                        if (!o || !opts.cache) {
                            o = new OrignalViewClass(name, options);
                        }
                        if (!o.path) {
                            throw new Error('Failed to lookup view "' + name + '"');
                        }
                        // render using the original implemented from express which
                        // is going to try to use the filesystem and the view engines
                        o.render(opts, fn);
                        return;
                    }
                    fn.apply(this, arguments);
                });
            } catch (err) {
                err.view = v;
                fn(err);
            }
        };
    }

    // Modifies the Express `app`.
    app.set('view', ViewShim);

    return app;
}

exports.extend = expressView;
<|endoftext|>


<commit_before>// http://www.myersdaily.org/joseph/javascript/md5-text.html
(function() {

	function md5cycle(x, k) {
		var a = x[0], b = x[1], c = x[2], d = x[3];

		a = ff(a, b, c, d, k[0], 7, -680876936);
		d = ff(d, a, b, c, k[1], 12, -389564586);
		c = ff(c, d, a, b, k[2], 17,  606105819);
		b = ff(b, c, d, a, k[3], 22, -1044525330);
		a = ff(a, b, c, d, k[4], 7, -176418897);
		d = ff(d, a, b, c, k[5], 12,  1200080426);
		c = ff(c, d, a, b, k[6], 17, -1473231341);
		b = ff(b, c, d, a, k[7], 22, -45705983);
		a = ff(a, b, c, d, k[8], 7,  1770035416);
		d = ff(d, a, b, c, k[9], 12, -1958414417);
		c = ff(c, d, a, b, k[10], 17, -42063);
		b = ff(b, c, d, a, k[11], 22, -1990404162);
		a = ff(a, b, c, d, k[12], 7,  1804603682);
		d = ff(d, a, b, c, k[13], 12, -40341101);
		c = ff(c, d, a, b, k[14], 17, -1502002290);
		b = ff(b, c, d, a, k[15], 22,  1236535329);

		a = gg(a, b, c, d, k[1], 5, -165796510);
		d = gg(d, a, b, c, k[6], 9, -1069501632);
		c = gg(c, d, a, b, k[11], 14,  643717713);
		b = gg(b, c, d, a, k[0], 20, -373897302);
		a = gg(a, b, c, d, k[5], 5, -701558691);
		d = gg(d, a, b, c, k[10], 9,  38016083);
		c = gg(c, d, a, b, k[15], 14, -660478335);
		b = gg(b, c, d, a, k[4], 20, -405537848);
		a = gg(a, b, c, d, k[9], 5,  568446438);
		d = gg(d, a, b, c, k[14], 9, -1019803690);
		c = gg(c, d, a, b, k[3], 14, -187363961);
		b = gg(b, c, d, a, k[8], 20,  1163531501);
		a = gg(a, b, c, d, k[13], 5, -1444681467);
		d = gg(d, a, b, c, k[2], 9, -51403784);
		c = gg(c, d, a, b, k[7], 14,  1735328473);
		b = gg(b, c, d, a, k[12], 20, -1926607734);

		a = hh(a, b, c, d, k[5], 4, -378558);
		d = hh(d, a, b, c, k[8], 11, -2022574463);
		c = hh(c, d, a, b, k[11], 16,  1839030562);
		b = hh(b, c, d, a, k[14], 23, -35309556);
		a = hh(a, b, c, d, k[1], 4, -1530992060);
		d = hh(d, a, b, c, k[4], 11,  1272893353);
		c = hh(c, d, a, b, k[7], 16, -155497632);
		b = hh(b, c, d, a, k[10], 23, -1094730640);
		a = hh(a, b, c, d, k[13], 4,  681279174);
		d = hh(d, a, b, c, k[0], 11, -358537222);
		c = hh(c, d, a, b, k[3], 16, -722521979);
		b = hh(b, c, d, a, k[6], 23,  76029189);
		a = hh(a, b, c, d, k[9], 4, -640364487);
		d = hh(d, a, b, c, k[12], 11, -421815835);
		c = hh(c, d, a, b, k[15], 16,  530742520);
		b = hh(b, c, d, a, k[2], 23, -995338651);

		a = ii(a, b, c, d, k[0], 6, -198630844);
		d = ii(d, a, b, c, k[7], 10,  1126891415);
		c = ii(c, d, a, b, k[14], 15, -1416354905);
		b = ii(b, c, d, a, k[5], 21, -57434055);
		a = ii(a, b, c, d, k[12], 6,  1700485571);
		d = ii(d, a, b, c, k[3], 10, -1894986606);
		c = ii(c, d, a, b, k[10], 15, -1051523);
		b = ii(b, c, d, a, k[1], 21, -2054922799);
		a = ii(a, b, c, d, k[8], 6,  1873313359);
		d = ii(d, a, b, c, k[15], 10, -30611744);
		c = ii(c, d, a, b, k[6], 15, -1560198380);
		b = ii(b, c, d, a, k[13], 21,  1309151649);
		a = ii(a, b, c, d, k[4], 6, -145523070);
		d = ii(d, a, b, c, k[11], 10, -1120210379);
		c = ii(c, d, a, b, k[2], 15,  718787259);
		b = ii(b, c, d, a, k[9], 21, -343485551);

		x[0] = add32(a, x[0]);
		x[1] = add32(b, x[1]);
		x[2] = add32(c, x[2]);
		x[3] = add32(d, x[3]);
	}

	function cmn(q, a, b, x, s, t) {
		a = add32(add32(a, q), add32(x, t));
		return add32((a << s) | (a >>> (32 - s)), b);
	}

	function ff(a, b, c, d, x, s, t) {
		return cmn((b & c) | ((~b) & d), a, b, x, s, t);
	}

	function gg(a, b, c, d, x, s, t) {
		return cmn((b & d) | (c & (~d)), a, b, x, s, t);
	}

	function hh(a, b, c, d, x, s, t) {
		return cmn(b ^ c ^ d, a, b, x, s, t);
	}

	function ii(a, b, c, d, x, s, t) {
		return cmn(c ^ (b | (~d)), a, b, x, s, t);
	}

	function md51(s) {
		txt = '';
		var n = s.length,
		state = [1732584193, -271733879, -1732584194, 271733878], i;
		for (i=64; i<=s.length; i+=64) {
			md5cycle(state, md5blk(s.substring(i-64, i)));
		}

		s = s.substring(i-64);
		var tail = [0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0];
		for (i=0; i<s.length; i++)
		tail[i>>2] |= s.charCodeAt(i) << ((i%4) << 3);
		tail[i>>2] |= 0x80 << ((i%4) << 3);
		if (i > 55) {
			md5cycle(state, tail);
			for (i=0; i<16; i++) tail[i] = 0;
		}
		tail[14] = n*8;
		md5cycle(state, tail);
		return state;
	}

	/* there needs to be support for Unicode here,
	 * unless we pretend that we can redefine the MD-5
	 * algorithm for multi-byte characters (perhaps
	 * by adding every four 16-bit characters and
	 * shortening the sum to 32 bits). Otherwise
	 * I suggest performing MD-5 as if every character
	 * was two bytes--e.g., 0040 0025 = @%--but then
	 * how will an ordinary MD-5 sum be matched?
	 * There is no way to standardize text to something
	 * like UTF-8 before transformation; speed cost is
	 * utterly prohibitive. The JavaScript standard
	 * itself needs to look at this: it should start
	 * providing access to strings as preformed UTF-8
	 * 8-bit unsigned value arrays.
	 */
	function md5blk(s) { /* I figured global was faster.   */
		var md5blks = [], i; /* Andy King said do it this way. */
		for (i=0; i<64; i+=4) {
			md5blks[i>>2] = s.charCodeAt(i)
			+ (s.charCodeAt(i+1) << 8)
			+ (s.charCodeAt(i+2) << 16)
			+ (s.charCodeAt(i+3) << 24);
		}
		return md5blks;
	}

	var hex_chr = '0123456789abcdef'.split('');

	function rhex(n)
	{
		var s='', j=0;
		for(; j<4; j++)
		s += hex_chr[(n >> (j * 8 + 4)) & 0x0F]
		+ hex_chr[(n >> (j * 8)) & 0x0F];
		return s;
	}

	function hex(x) {
		for (var i=0; i<x.length; i++)
			x[i] = rhex(x[i]);
		return x.join('');
	}

	function md5(s) {
		return hex(md51(s));
	}

	/* this function is much faster,
	so if possible we use it. Some IEs
	are the only ones I know of that
	need the idiotic second function,
	generated by an if clause.  */

	function add32(a, b) {
		return (a + b) & 0xFFFFFFFF;
	}

	if (md5('hello') != '5d41402abc4b2a76b9719d911017c592') {
		function add32(x, y) {
			var lsw = (x & 0xFFFF) + (y & 0xFFFF),
			msw = (x >> 16) + (y >> 16) + (lsw >> 16);
			return (msw << 16) | (lsw & 0xFFFF);
		}
	}

	angular.module('kodex')
	.factory('md5', function() {
		return md5;
	})
	.filter('md5', function() {
		return function(input) {
			return md5(input);
		};
	})

})(angular);<commit_msg>Check input before attempting the md5 hash<commit_after>// http://www.myersdaily.org/joseph/javascript/md5-text.html
(function() {

	function md5cycle(x, k) {
		var a = x[0], b = x[1], c = x[2], d = x[3];

		a = ff(a, b, c, d, k[0], 7, -680876936);
		d = ff(d, a, b, c, k[1], 12, -389564586);
		c = ff(c, d, a, b, k[2], 17,  606105819);
		b = ff(b, c, d, a, k[3], 22, -1044525330);
		a = ff(a, b, c, d, k[4], 7, -176418897);
		d = ff(d, a, b, c, k[5], 12,  1200080426);
		c = ff(c, d, a, b, k[6], 17, -1473231341);
		b = ff(b, c, d, a, k[7], 22, -45705983);
		a = ff(a, b, c, d, k[8], 7,  1770035416);
		d = ff(d, a, b, c, k[9], 12, -1958414417);
		c = ff(c, d, a, b, k[10], 17, -42063);
		b = ff(b, c, d, a, k[11], 22, -1990404162);
		a = ff(a, b, c, d, k[12], 7,  1804603682);
		d = ff(d, a, b, c, k[13], 12, -40341101);
		c = ff(c, d, a, b, k[14], 17, -1502002290);
		b = ff(b, c, d, a, k[15], 22,  1236535329);

		a = gg(a, b, c, d, k[1], 5, -165796510);
		d = gg(d, a, b, c, k[6], 9, -1069501632);
		c = gg(c, d, a, b, k[11], 14,  643717713);
		b = gg(b, c, d, a, k[0], 20, -373897302);
		a = gg(a, b, c, d, k[5], 5, -701558691);
		d = gg(d, a, b, c, k[10], 9,  38016083);
		c = gg(c, d, a, b, k[15], 14, -660478335);
		b = gg(b, c, d, a, k[4], 20, -405537848);
		a = gg(a, b, c, d, k[9], 5,  568446438);
		d = gg(d, a, b, c, k[14], 9, -1019803690);
		c = gg(c, d, a, b, k[3], 14, -187363961);
		b = gg(b, c, d, a, k[8], 20,  1163531501);
		a = gg(a, b, c, d, k[13], 5, -1444681467);
		d = gg(d, a, b, c, k[2], 9, -51403784);
		c = gg(c, d, a, b, k[7], 14,  1735328473);
		b = gg(b, c, d, a, k[12], 20, -1926607734);

		a = hh(a, b, c, d, k[5], 4, -378558);
		d = hh(d, a, b, c, k[8], 11, -2022574463);
		c = hh(c, d, a, b, k[11], 16,  1839030562);
		b = hh(b, c, d, a, k[14], 23, -35309556);
		a = hh(a, b, c, d, k[1], 4, -1530992060);
		d = hh(d, a, b, c, k[4], 11,  1272893353);
		c = hh(c, d, a, b, k[7], 16, -155497632);
		b = hh(b, c, d, a, k[10], 23, -1094730640);
		a = hh(a, b, c, d, k[13], 4,  681279174);
		d = hh(d, a, b, c, k[0], 11, -358537222);
		c = hh(c, d, a, b, k[3], 16, -722521979);
		b = hh(b, c, d, a, k[6], 23,  76029189);
		a = hh(a, b, c, d, k[9], 4, -640364487);
		d = hh(d, a, b, c, k[12], 11, -421815835);
		c = hh(c, d, a, b, k[15], 16,  530742520);
		b = hh(b, c, d, a, k[2], 23, -995338651);

		a = ii(a, b, c, d, k[0], 6, -198630844);
		d = ii(d, a, b, c, k[7], 10,  1126891415);
		c = ii(c, d, a, b, k[14], 15, -1416354905);
		b = ii(b, c, d, a, k[5], 21, -57434055);
		a = ii(a, b, c, d, k[12], 6,  1700485571);
		d = ii(d, a, b, c, k[3], 10, -1894986606);
		c = ii(c, d, a, b, k[10], 15, -1051523);
		b = ii(b, c, d, a, k[1], 21, -2054922799);
		a = ii(a, b, c, d, k[8], 6,  1873313359);
		d = ii(d, a, b, c, k[15], 10, -30611744);
		c = ii(c, d, a, b, k[6], 15, -1560198380);
		b = ii(b, c, d, a, k[13], 21,  1309151649);
		a = ii(a, b, c, d, k[4], 6, -145523070);
		d = ii(d, a, b, c, k[11], 10, -1120210379);
		c = ii(c, d, a, b, k[2], 15,  718787259);
		b = ii(b, c, d, a, k[9], 21, -343485551);

		x[0] = add32(a, x[0]);
		x[1] = add32(b, x[1]);
		x[2] = add32(c, x[2]);
		x[3] = add32(d, x[3]);
	}

	function cmn(q, a, b, x, s, t) {
		a = add32(add32(a, q), add32(x, t));
		return add32((a << s) | (a >>> (32 - s)), b);
	}

	function ff(a, b, c, d, x, s, t) {
		return cmn((b & c) | ((~b) & d), a, b, x, s, t);
	}

	function gg(a, b, c, d, x, s, t) {
		return cmn((b & d) | (c & (~d)), a, b, x, s, t);
	}

	function hh(a, b, c, d, x, s, t) {
		return cmn(b ^ c ^ d, a, b, x, s, t);
	}

	function ii(a, b, c, d, x, s, t) {
		return cmn(c ^ (b | (~d)), a, b, x, s, t);
	}

	function md51(s) {
		txt = '';
		var n = s.length,
		state = [1732584193, -271733879, -1732584194, 271733878], i;
		for (i=64; i<=s.length; i+=64) {
			md5cycle(state, md5blk(s.substring(i-64, i)));
		}

		s = s.substring(i-64);
		var tail = [0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0];
		for (i=0; i<s.length; i++)
		tail[i>>2] |= s.charCodeAt(i) << ((i%4) << 3);
		tail[i>>2] |= 0x80 << ((i%4) << 3);
		if (i > 55) {
			md5cycle(state, tail);
			for (i=0; i<16; i++) tail[i] = 0;
		}
		tail[14] = n*8;
		md5cycle(state, tail);
		return state;
	}

	/* there needs to be support for Unicode here,
	 * unless we pretend that we can redefine the MD-5
	 * algorithm for multi-byte characters (perhaps
	 * by adding every four 16-bit characters and
	 * shortening the sum to 32 bits). Otherwise
	 * I suggest performing MD-5 as if every character
	 * was two bytes--e.g., 0040 0025 = @%--but then
	 * how will an ordinary MD-5 sum be matched?
	 * There is no way to standardize text to something
	 * like UTF-8 before transformation; speed cost is
	 * utterly prohibitive. The JavaScript standard
	 * itself needs to look at this: it should start
	 * providing access to strings as preformed UTF-8
	 * 8-bit unsigned value arrays.
	 */
	function md5blk(s) { /* I figured global was faster.   */
		var md5blks = [], i; /* Andy King said do it this way. */
		for (i=0; i<64; i+=4) {
			md5blks[i>>2] = s.charCodeAt(i)
			+ (s.charCodeAt(i+1) << 8)
			+ (s.charCodeAt(i+2) << 16)
			+ (s.charCodeAt(i+3) << 24);
		}
		return md5blks;
	}

	var hex_chr = '0123456789abcdef'.split('');

	function rhex(n)
	{
		var s='', j=0;
		for(; j<4; j++)
		s += hex_chr[(n >> (j * 8 + 4)) & 0x0F]
		+ hex_chr[(n >> (j * 8)) & 0x0F];
		return s;
	}

	function hex(x) {
		for (var i=0; i<x.length; i++)
			x[i] = rhex(x[i]);
		return x.join('');
	}

	function md5(s) {
		return hex(md51(s));
	}

	/* this function is much faster,
	so if possible we use it. Some IEs
	are the only ones I know of that
	need the idiotic second function,
	generated by an if clause.  */

	function add32(a, b) {
		return (a + b) & 0xFFFFFFFF;
	}

	if (md5('hello') != '5d41402abc4b2a76b9719d911017c592') {
		function add32(x, y) {
			var lsw = (x & 0xFFFF) + (y & 0xFFFF),
			msw = (x >> 16) + (y >> 16) + (lsw >> 16);
			return (msw << 16) | (lsw & 0xFFFF);
		}
	}

	angular.module('kodex')
	.factory('md5', function() {
		return md5;
	})
	.filter('md5', function() {
		return function(input) {
			if ( input )
				return md5(input);
			else
				return '';
		};
	})

})(angular);<|endoftext|>


<commit_before>class Map extends graphlib.Graph {
    constructor (game) {

		super({ directed: false });

		this.rootNode = null;

		var prevNode = null;
	    for ( var i=0; i<Map.CITY_COUNT; i++ ) {

			name = Map.CITY_NAMES.pop();
		    var newNode = new City(name, Math.random(), Math.random(), game);
            this.setNode(name, newNode);
		    if ( this.rootNode == null ) {
			    //must be the first node we've ever created
			    console.log("ROOT NODE is " + newNode.name);
			    this.rootNode = newNode;
		    }
        }

		this.generateCityFullMesh();
		console.log("EDGES: " + this.edges().length);
		this.removeIntersectingPaths();
		console.log("EDGES: " + this.edges().length);
		console.log(this.edges());

		console.log("Connected cities for " + this.rootNode.name + ":");
		console.log(this.getConnectedCities(this.rootNode));

    }
    getCities() {
        return this._nodes;
    }
    getClosestCity(target, orphanOnly) {
		if ( typeof orphanOnly === undefined ) {
			orphanOnly = false;
		}

		var cities = this.getCities();
		var closest = cities[0];
        for ( var idx in cities ) {
			if ( getCityDistance(target,cities[idx]) < getCityDistance(target,closest) ) {
				if ( orphanOnly === false || this.nodeEdges(target.name) ) { //if we don't care whether it's an orphan, or if it is an orphan
					closest = cities[idx];
				}
			}
		}
		return closest;
	}
	getConnectedCities(target) {
		var neigh = this.neighbors(target.name);
		neigh.map(function(val,idx,arr) {
			arr[idx] = this.node(val);
		},this);
		return neigh;
	}
	generateCityFullMesh() {
		var cities = this.getCities();
		for ( var idx in cities ) {
			for ( var idx2 in cities ) {
				if ( idx != idx2 && !this.hasEdge(cities[idx].name,cities[idx2].name) ) {
					this.setEdge(cities[idx].name, cities[idx2].name, cities[idx].name + " - " + cities[idx2].name);
					console.log(this.edge(cities[idx].name,cities[idx2].name));
				}
			}
		}
	}
	removeIntersectingPaths() {
		var edges = this.edges();
		for ( var idx in edges ) {
			var v1 = edges[idx].v;
			var w1 = edges[idx].w;
			for ( var idx2 in edges ) {
				var v2 = edges[idx2].v;
				var w2 = edges[idx2].w;
				if ( idx != idx2 && this.hasEdge(v1,w1) && this.hasEdge(v2,w2) && this.doCityPathsIntersect(this.node(v1), this.node(w1), this.node(v2), this.node(w2)) ) {
					console.log("Remove conflicting edge " + v2 + "," + w2);
					this.removeEdge(v2,w2);
				}
			}
		}
	}

	ccw(A,B,C) {
		return ((C.y-A.y) * (B.x-A.x)) > ((B.y-A.y) * (C.x-A.x));
	}
	doCityPathsIntersect(A,B,C,D) {
		if ( A==C || A==D || B==C || B==D ) {
			return false;
		}
		console.log("Check " + A.name + "," + B.name + " vs " + C.name + "," + D.name);
		return (this.ccw(A,C,D) != this.ccw(B,C,D)) && (this.ccw(A,B,C) != this.ccw(A,B,D));
	}

	getCityDistance(a,b) {
		return Math.hypot(a.x-b.x, a.y-b.y); //lol
	}

}

Map.CITY_COUNT = 10;
Map.CITY_NAMES = knuthShuffle([
		"Arcola",
		"Bainbridge",
		"Barnesville",
		"Battelle",
		"Beaver Mills",
		"Bellefonte",
		"Blakeley",
		"Blanche",
		"Bluff City",
		"Bluffton",
		"Boston",
		"Brownville",
		"Cahaba",
		"Cedric",
		"Centerdale",
		"Chandler Springs",
		"Choctaw Corner",
		"Claiborne",
		"Clarkesville",
		"Dumphries",
		"Erie",
		"Failetown",
		"Finchburg",
		"Gantts Quarry",
		"Gold Log Mine",
		"Houston",
		"Kaulton",
		"Louina",
		"Manasco",
		"Massillon",
		"Minden",
		"Montezuma",
		"Morgan Stream",
		"Mountain Mills",
		"Nottingham",
		"Odena",
		"Old Ramer",
		"Pansey",
		"Pikeville",
		"Prairie Bluff",
		"Riverton",
		"Rockcastle",
		"St. Stephens",
		"Stanton",
		"Tooktocaugee",
		"Turkey Town",
		"Valhermoso Springs",
		"Vienna",
		"Waldo",
		])

<commit_msg>Add getRoads()<commit_after>class Map extends graphlib.Graph {
    constructor (game) {

		super({ directed: false });

		this.rootNode = null;

		var prevNode = null;
	    for ( var i=0; i<Map.CITY_COUNT; i++ ) {

			name = Map.CITY_NAMES.pop();
		    var newNode = new City(name, Math.random(), Math.random(), game);
            this.setNode(name, newNode);
		    if ( this.rootNode == null ) {
			    //must be the first node we've ever created
			    console.log("ROOT NODE is " + newNode.name);
			    this.rootNode = newNode;
		    }
        }

		this.generateCityFullMesh();
		//console.log("EDGES: " + this.edges().length);
		this.removeIntersectingPaths();
		//console.log("EDGES: " + this.edges().length);
		//console.log(this.edges());

		//console.log("Connected cities for " + this.rootNode.name + ":");
		//console.log(this.getConnectedCities(this.rootNode));

		console.log("All roads");
		console.log(this.getRoads());

    }
    getCities() {
        return this._nodes;
    }
    getClosestCity(target, orphanOnly) {
		if ( typeof orphanOnly === undefined ) {
			orphanOnly = false;
		}

		var cities = this.getCities();
		var closest = cities[0];
        for ( var idx in cities ) {
			if ( getCityDistance(target,cities[idx]) < getCityDistance(target,closest) ) {
				if ( orphanOnly === false || this.nodeEdges(target.name) ) { //if we don't care whether it's an orphan, or if it is an orphan
					closest = cities[idx];
				}
			}
		}
		return closest;
	}
	getConnectedCities(target) {
		var neigh = this.neighbors(target.name);
		neigh.map(function(val,idx,arr) {
			arr[idx] = this.node(val);
		},this);
		return neigh;
	}
	getRoads() {
		var roads = this.edges();
		roads.map(function(val,idx,arr) {
			arr[idx] = {
				v: this.node(val.v),
				w: this.node(val.w)
			};
		},this);
		return roads;
	}
	generateCityFullMesh() {
		var cities = this.getCities();
		for ( var idx in cities ) {
			for ( var idx2 in cities ) {
				if ( idx != idx2 && !this.hasEdge(cities[idx].name,cities[idx2].name) ) {
					this.setEdge(cities[idx].name, cities[idx2].name, cities[idx].name + " - " + cities[idx2].name);
					//console.log(this.edge(cities[idx].name,cities[idx2].name));
				}
			}
		}
	}
	removeIntersectingPaths() {
		var edges = this.edges();
		for ( var idx in edges ) {
			var v1 = edges[idx].v;
			var w1 = edges[idx].w;
			for ( var idx2 in edges ) {
				var v2 = edges[idx2].v;
				var w2 = edges[idx2].w;
				if ( idx != idx2 && this.hasEdge(v1,w1) && this.hasEdge(v2,w2) && this.doCityPathsIntersect(this.node(v1), this.node(w1), this.node(v2), this.node(w2)) ) {
					//console.log("Remove conflicting edge " + v2 + "," + w2);
					this.removeEdge(v2,w2);
				}
			}
		}
	}

	ccw(A,B,C) {
		return ((C.y-A.y) * (B.x-A.x)) > ((B.y-A.y) * (C.x-A.x));
	}
	doCityPathsIntersect(A,B,C,D) {
		if ( A==C || A==D || B==C || B==D ) {
			return false;
		}
		//console.log("Check " + A.name + "," + B.name + " vs " + C.name + "," + D.name);
		return (this.ccw(A,C,D) != this.ccw(B,C,D)) && (this.ccw(A,B,C) != this.ccw(A,B,D));
	}

	getCityDistance(a,b) {
		return Math.hypot(a.x-b.x, a.y-b.y); //lol
	}

}

Map.CITY_COUNT = 10;
Map.CITY_NAMES = knuthShuffle([
		"Arcola",
		"Bainbridge",
		"Barnesville",
		"Battelle",
		"Beaver Mills",
		"Bellefonte",
		"Blakeley",
		"Blanche",
		"Bluff City",
		"Bluffton",
		"Boston",
		"Brownville",
		"Cahaba",
		"Cedric",
		"Centerdale",
		"Chandler Springs",
		"Choctaw Corner",
		"Claiborne",
		"Clarkesville",
		"Dumphries",
		"Erie",
		"Failetown",
		"Finchburg",
		"Gantts Quarry",
		"Gold Log Mine",
		"Houston",
		"Kaulton",
		"Louina",
		"Manasco",
		"Massillon",
		"Minden",
		"Montezuma",
		"Morgan Stream",
		"Mountain Mills",
		"Nottingham",
		"Odena",
		"Old Ramer",
		"Pansey",
		"Pikeville",
		"Prairie Bluff",
		"Riverton",
		"Rockcastle",
		"St. Stephens",
		"Stanton",
		"Tooktocaugee",
		"Turkey Town",
		"Valhermoso Springs",
		"Vienna",
		"Waldo",
		])

<|endoftext|>


<commit_before>import Component from '@glimmer/component';
import { action } from "@ember/object";
import { debug } from '@ember/debug';
import { guidFor } from '@ember/object/internals';

import uPlot from 'uplot';
import seriesBarsPlugin from './uplot/seriesbars-plugin';

//import 'uplot/dist/uPlot.min.css';
//import wheelZoom from './chart/plugin-wheelzoom.js'

/**
...

```html
<OxiBase::Chart .../>
```

@module oxi-base/chart
@param list { array } - List of hashes defining the options.
*/

export default class OxiChartComponent extends Component {
    guid;
    options;
    data;

    // x - timestamp
    // y - BTC price
    // y - RSI
    // y - RSI MA

    constructor() {
        super(...arguments);

        this.guid = guidFor(this);
        /*
         * Options
         */
        // Evaluate given options and set defaults
        const {
            width = 400,
            height = 200,
            title = "",
            cssClass = "",
            x_is_timestamp = true,
            y_axes = [ {} ], // default: one array item to make sure the loop below creates left Y-axis
            legend_label = (this.args.options.y_axes ? true : false),
            legend_value = false,
            legend_date_format = '{YYYY}-{MM}-{DD}, {HH}:{mm}:{ss}',
            type = 'line',
            bar_group_labels,
            bar_vertical = false,
        } = this.args.options ?? {};

        // assemble uPlot options
        let uplotOptions = {
            width,
            height,
            title,
            class: cssClass,
            legend: {
                show: legend_label,
                live: legend_value,
            },
            scales: {
                x: {
                    time: x_is_timestamp,
                },
                'auto': {
                    auto: true,
                },
                '%': {
                    auto: false,
                    range: (self) => [ 0, 100 ],
                },
            },
            series: [
                {}, // x values
            ],
            axes: [
                {
                    time: true,
                    values: this.axisTimestampConfig(),
                }, // x axis
            ],
        };

        /*
         * LINE chart
         */
        // set custom date format
        if (type == 'line' && x_is_timestamp) {
            // format strings: https://github.com/leeoniya/uPlot/blob/1.6.3/src/fmtDate.js#L74
            let dateFormatter = uPlot.fmtDate(legend_date_format);
            uplotOptions.series[0].value = (self, rawValue) => rawValue == null ? "-" : dateFormatter(new Date(rawValue * 1000));
        }

        /*
         * BAR chart
         */
        if (type == 'bar') {
            uPlot.assign(uplotOptions, {
                scales: {
                    x: {
                        time: false,
                        values: undefined,
                    },
                },
                plugins: [
                    seriesBarsPlugin({
                        labels: () => this.args.data.map(group => bar_group_labels ? bar_group_labels[group[0]] : group[0]), // group / time series
                        ori: bar_vertical ? 1 : 0,
                        dir: 1,
                    }),
                ],
            });
        }

        /*
         * Series
         */
        let autoScaleId = 0;
        for (const graph of y_axes) {
            const {
                label = '',
                color = 'rgba(0, 100, 200, 1)',
                fill,
                line_width = 1,
                scale = 'auto',
            } = graph;

            // Auto-generate scale if an array [min, max] was specified
            let _scale = scale;
            if (Array.isArray(scale)) {
                _scale = `_autogenerated_${++autoScaleId}`;
                uplotOptions.scales[_scale] = {
                    auto: false,
                    range: () => scale,
                };
            }

            let seriesOpts = {
                label,
                scale: _scale,
                fill: fill ?? this.reducedAlphaColor(color),
                width: line_width/window.devicePixelRatio,
                //value: (self, rawValue) => rawValue == null ? "-" : rawValue.toFixed(0),
            }
            if (type == 'line') {
                seriesOpts.stroke = color;
            }
            if (type == 'bar') {
                seriesOpts.fill = color;
            }
            uplotOptions.series.push(seriesOpts);

            // add up to 2 axis (left and right)
            if (uplotOptions.axes.length < 3) {
                let axis = {
                    scale: _scale,
                    space: Math.max((bar_vertical ? width : height) / 20, 15),
                    //labelSize: 150,
                    size: 60,
                    stroke: type == 'bar' ? 'black' : color,
                };
                // special treatment for percent
                if (_scale == '%') uPlot.assign(axis, {
                    values: (u, vals, space) => vals.map(v => `${v.toFixed(0)}%`),
                });
                // second axis
                if (uplotOptions.axes.length == 2) {
                    // skip if same scale as first axis
                    if (uplotOptions.axes[1].scale == _scale) continue;
                    // right hand side, no grid
                    uPlot.assign(axis, {
                        side: 1,
                        grid: { show: false },
                    })
                }
                uplotOptions.axes.push(axis);
            }
        }

        this.options = uplotOptions;

        /*
         * Convert data from
         * from [ [x1, price1, rsi1], [x2, price2, rsi2] ]
         *   to [ [x1, x2], [price1, price2], [rsi1, rsi2] ]
         */
        this.data = [];
        for (let i=0; i<this.args.data[0].length; i++) {
            this.data.push(this.args.data.map(row => +row[i]));
        }
    }

    @action
    plot(element) {
        new uPlot(this.options, this.data, (uplot, init) => {
            element.appendChild(uplot.root);
            init();
        })
    }

    reducedAlphaColor(cssColor) {
        const div = document.createElement('div');
        div.id = 'for-computed-style';

        div.style.color = cssColor;

        // appending the created element to the DOM
        document.querySelector('body').appendChild(div);

        const match = getComputedStyle(div).color.match(/^rgba?\s*\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d\.\d)\s*)?\)$/i);

        // removing element from the DOM
        document.querySelector('#for-computed-style').remove();

        if (match) {
            // match[0] is regex complete match (e.g. "rgb(0,0,0)"), not a regex capturing group
            let col = {
                r: match[1],
                g: match[2],
                b: match[3]
            };
            // if (match[4]) { // if alpha channel is present
            //     parsedColor.a = match[4];
            // }
            return `rgba(${col.r},${col.g},${col.b},0.1)`;
        } else {
            throw new Error(`Color ${cssColor} could not be parsed.`);
        }
    }

    // From https://github.com/leeoniya/uPlot/blob/1.6.3/src/opts.js#L65
    axisTimestampConfig() {
        const ms = 1e-3; // 1e-3 = set up for seconds, 1 = set up for milliseconds

        const NL = "\n";

        const yyyy    = "{YYYY}";
        const NLyyyy  = NL + yyyy;
        const md      = "{DD}.{MM}";
        const NLmd    = NL + md;
        const NLmdyy  = NL + "{DD}.{MM}.{YY}";

        const hmm     = "{H}:{mm}";
        const NLhmm = NL + hmm;
        const ss      = ":{ss}";

        const _ = null;

        const s  = ms * 1e3,
              m  = s  * 60,
              h  = m  * 60,
              d  = h  * 24,
              mo = d  * 30,
              y  = d  * 365;

        // [0]:   minimum num secs in the tick incr
        // [1]:   default tick format
        // [2-7]: rollover tick formats
        // [8]:   mode: 0: replace [1] -> [2-7], 1: concat [1] + [2-7]
        return [
        //   tick incr    default          year                    month   day                   hour    min       sec   mode
            [y,           yyyy,            _,                      _,      _,                    _,      _,        _,       1],
            [d * 28,      "{MMM}",         NLyyyy,                 _,      _,                    _,      _,        _,       1],
            [d,           md,              NLyyyy,                 _,      _,                    _,      _,        _,       1],
            [h,           "{H}",           NLmdyy,                 _,      NLmd,                 _,      _,        _,       1],
            [m,           hmm,             NLmdyy,                 _,      NLmd,                 _,      _,        _,       1],
            [s,           ss,              NLmdyy + " " + hmm,     _,      NLmd + " " + hmm,     _,      NLhmm,    _,       1],
            [ms,          ss + ".{fff}",   NLmdyy + " " + hmm,     _,      NLmd + " " + hmm,     _,      NLhmm,    _,       1],
        ];
    }
}
<commit_msg>Allow to leave 'y_axes' (future 'series') option for bar charts<commit_after>import Component from '@glimmer/component';
import { action } from "@ember/object";
import { debug } from '@ember/debug';
import { guidFor } from '@ember/object/internals';

import uPlot from 'uplot';
import seriesBarsPlugin from './uplot/seriesbars-plugin';

//import 'uplot/dist/uPlot.min.css';
//import wheelZoom from './chart/plugin-wheelzoom.js'

/**
...

```html
<OxiBase::Chart .../>
```

@module oxi-base/chart
@param list { array } - List of hashes defining the options.
*/

export default class OxiChartComponent extends Component {
    guid;
    options;
    data;

    // x - timestamp
    // y - BTC price
    // y - RSI
    // y - RSI MA

    constructor() {
        super(...arguments);

        this.guid = guidFor(this);
        /*
         * Options
         */
        // Evaluate given options and set defaults
        const {
            width = 400,
            height = 200,
            title = "",
            cssClass = "",
            x_is_timestamp = true,
            y_axes = [ {} ], // default: one array item to make sure the loop below creates left Y-axis
            legend_label = (this.args.options.y_axes ? true : false),
            legend_value = false,
            legend_date_format = '{YYYY}-{MM}-{DD}, {HH}:{mm}:{ss}',
            type = 'line',
            bar_group_labels,
            bar_vertical = false,
        } = this.args.options ?? {};

        // assemble uPlot options
        let uplotOptions = {
            width,
            height,
            title,
            class: cssClass,
            legend: {
                show: legend_label,
                live: legend_value,
            },
            scales: {
                x: {
                    time: x_is_timestamp,
                },
                'auto': {
                    auto: true,
                },
                '%': {
                    auto: false,
                    range: (self) => [ 0, 100 ],
                },
            },
            series: [
                {}, // x values
            ],
            axes: [
                {
                    time: true,
                    values: this.axisTimestampConfig(),
                }, // x axis
            ],
        };

        /*
         * LINE chart
         */
        // set custom date format
        if (type == 'line' && x_is_timestamp) {
            // format strings: https://github.com/leeoniya/uPlot/blob/1.6.3/src/fmtDate.js#L74
            let dateFormatter = uPlot.fmtDate(legend_date_format);
            uplotOptions.series[0].value = (self, rawValue) => rawValue == null ? "-" : dateFormatter(new Date(rawValue * 1000));
        }

        /*
         * BAR chart
         */
        if (type == 'bar') {
            uPlot.assign(uplotOptions, {
                scales: {
                    x: {
                        time: false,
                        values: undefined,
                    },
                },
                plugins: [
                    seriesBarsPlugin({
                        labels: () => this.args.data.map(group => bar_group_labels ? bar_group_labels[group[0]] : group[0]), // group / time series
                        ori: bar_vertical ? 1 : 0,
                        dir: 1,
                    }),
                ],
            });
            // The series loop below needs the series to be defined
            if (!this.args.options.y_axes) {
                for (let i = 0; i < this.data[0].length; i++) y_axes.push({})
            }
        }

        /*
         * Series
         */
        let autoScaleId = 0;
        for (const graph of y_axes) {
            const {
                label = '',
                color = 'rgba(0, 100, 200, 1)',
                fill,
                line_width = 1,
                scale = 'auto',
            } = graph;

            // Auto-generate scale if an array [min, max] was specified
            let _scale = scale;
            if (Array.isArray(scale)) {
                _scale = `_autogenerated_${++autoScaleId}`;
                uplotOptions.scales[_scale] = {
                    auto: false,
                    range: () => scale,
                };
            }

            let seriesOpts = {
                label,
                scale: _scale,
                fill: fill ?? this.reducedAlphaColor(color),
                width: line_width/window.devicePixelRatio,
                //value: (self, rawValue) => rawValue == null ? "-" : rawValue.toFixed(0),
            }
            if (type == 'line') {
                seriesOpts.stroke = color;
            }
            if (type == 'bar') {
                seriesOpts.fill = color;
            }
            uplotOptions.series.push(seriesOpts);

            // add up to 2 axis (left and right)
            if (uplotOptions.axes.length < 3) {
                let axis = {
                    scale: _scale,
                    space: Math.max((bar_vertical ? width : height) / 20, 15),
                    //labelSize: 150,
                    size: 60,
                    stroke: type == 'bar' ? 'black' : color,
                };
                // special treatment for percent
                if (_scale == '%') uPlot.assign(axis, {
                    values: (u, vals, space) => vals.map(v => `${v.toFixed(0)}%`),
                });
                // second axis
                if (uplotOptions.axes.length == 2) {
                    // skip if same scale as first axis
                    if (uplotOptions.axes[1].scale == _scale) continue;
                    // right hand side, no grid
                    uPlot.assign(axis, {
                        side: 1,
                        grid: { show: false },
                    })
                }
                uplotOptions.axes.push(axis);
            }
        }

        this.options = uplotOptions;

        /*
         * Convert data from
         * from [ [x1, price1, rsi1], [x2, price2, rsi2] ]
         *   to [ [x1, x2], [price1, price2], [rsi1, rsi2] ]
         */
        this.data = [];
        for (let i=0; i<this.args.data[0].length; i++) {
            this.data.push(this.args.data.map(row => +row[i]));
        }
    }

    @action
    plot(element) {
        new uPlot(this.options, this.data, (uplot, init) => {
            element.appendChild(uplot.root);
            init();
        })
    }

    reducedAlphaColor(cssColor) {
        const div = document.createElement('div');
        div.id = 'for-computed-style';

        div.style.color = cssColor;

        // appending the created element to the DOM
        document.querySelector('body').appendChild(div);

        const match = getComputedStyle(div).color.match(/^rgba?\s*\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d\.\d)\s*)?\)$/i);

        // removing element from the DOM
        document.querySelector('#for-computed-style').remove();

        if (match) {
            // match[0] is regex complete match (e.g. "rgb(0,0,0)"), not a regex capturing group
            let col = {
                r: match[1],
                g: match[2],
                b: match[3]
            };
            // if (match[4]) { // if alpha channel is present
            //     parsedColor.a = match[4];
            // }
            return `rgba(${col.r},${col.g},${col.b},0.1)`;
        } else {
            throw new Error(`Color ${cssColor} could not be parsed.`);
        }
    }

    // From https://github.com/leeoniya/uPlot/blob/1.6.3/src/opts.js#L65
    axisTimestampConfig() {
        const ms = 1e-3; // 1e-3 = set up for seconds, 1 = set up for milliseconds

        const NL = "\n";

        const yyyy    = "{YYYY}";
        const NLyyyy  = NL + yyyy;
        const md      = "{DD}.{MM}";
        const NLmd    = NL + md;
        const NLmdyy  = NL + "{DD}.{MM}.{YY}";

        const hmm     = "{H}:{mm}";
        const NLhmm = NL + hmm;
        const ss      = ":{ss}";

        const _ = null;

        const s  = ms * 1e3,
              m  = s  * 60,
              h  = m  * 60,
              d  = h  * 24,
              mo = d  * 30,
              y  = d  * 365;

        // [0]:   minimum num secs in the tick incr
        // [1]:   default tick format
        // [2-7]: rollover tick formats
        // [8]:   mode: 0: replace [1] -> [2-7], 1: concat [1] + [2-7]
        return [
        //   tick incr    default          year                    month   day                   hour    min       sec   mode
            [y,           yyyy,            _,                      _,      _,                    _,      _,        _,       1],
            [d * 28,      "{MMM}",         NLyyyy,                 _,      _,                    _,      _,        _,       1],
            [d,           md,              NLyyyy,                 _,      _,                    _,      _,        _,       1],
            [h,           "{H}",           NLmdyy,                 _,      NLmd,                 _,      _,        _,       1],
            [m,           hmm,             NLmdyy,                 _,      NLmd,                 _,      _,        _,       1],
            [s,           ss,              NLmdyy + " " + hmm,     _,      NLmd + " " + hmm,     _,      NLhmm,    _,       1],
            [ms,          ss + ".{fff}",   NLmdyy + " " + hmm,     _,      NLmd + " " + hmm,     _,      NLhmm,    _,       1],
        ];
    }
}
<|endoftext|>


<commit_before>/*
 * grunt-http-upload
 * https://github.com/DiscoverGrunt/grunt-http-upload
 *
 * Copyright (c) 2013 Julien M
 * Licensed under the MIT license.
 */

'use strict';

module.exports = function(grunt) {

  var fs = require('fs');
  var request = require('request');

  grunt.registerMultiTask('http_upload', 'Upload files through POST/PUT HTTP request', function() {

    // Tell Grunt this task is asynchronous.
    var done = this.async();

    // Merge task-specific and/or target-specific options with these defaults.
    var options = this.options({
      rejectUnauthorized: true,
      method: 'POST',
      headers: {},
      url:    '',
      onComplete: function(data) {}
    });

    grunt.verbose.writeflags(options, 'Options');

    // Iterate over all specified file groups.
    this.files.forEach(function(f) {
      // there should be only 1 file in source, but just to make sure...
      var filepath = f.src[0];
      var field    = f.dest;

      // Warn on and remove invalid source files (if nonull was set).
      if (!grunt.file.exists(filepath)) {
        grunt.fail.warn('Source file "' + filepath + '" not found.');
        return false;
      }

      // get file size (necessary for multipart upload)
      fs.stat(filepath, function(err, stats) {
        if (err) {
          grunt.fail.warn('Error: ' + err);
          done(err);
        } else if (stats.isFile()) {
          var fileSize = stats.size;
          grunt.log.writeln('Uploading "' + filepath + '" as "' + field + '"');
          // build request data (because of dynamic key in data object)
          var reqData = options.data || {};
          reqData[field] = fs.createReadStream(filepath);
          // Build the HTTP request
          request({
            url:options.url,
            strictSSL: options.rejectUnauthorized,
            method: options.method,
            headers: options.headers,
            formData: reqData
          }, function optionalCallback(err, response, data) {
            if (err) {
              return grunt.fail.warn('Failed uploading (error code: ' + err + ')');
            }
            if (response !== null && response.statusCode >= 200 && response.statusCode < 300) {
              grunt.log.ok('Upload successful of "' + filepath + '" as "' + field + '" - ' + options.method + ' @ ' + options.url);
              options.onComplete(data);
            } else if (response !== null) {
              grunt.fail.warn('Failed uploading "' + filepath + '" as "' + field + '" (status code: ' + response.statusCode + ') - ' + options.method + ' @ ' + options.url);
            } else {
              grunt.fail.warn('Failed uploading "' + filepath + '" as "' + field + '" (status code: null) - ' + options.method + ' @ ' + options.url);
            }
            // callback once upload is done
            done(data);
          });
        }
      });
    });
  });
};
<commit_msg>Follow all redirects, including non-GET.<commit_after>/*
 * grunt-http-upload
 * https://github.com/DiscoverGrunt/grunt-http-upload
 *
 * Copyright (c) 2013 Julien M
 * Licensed under the MIT license.
 */

'use strict';

module.exports = function(grunt) {

  var fs = require('fs');
  var request = require('request');

  grunt.registerMultiTask('http_upload', 'Upload files through POST/PUT HTTP request', function() {

    // Tell Grunt this task is asynchronous.
    var done = this.async();

    // Merge task-specific and/or target-specific options with these defaults.
    var options = this.options({
      rejectUnauthorized: true,
      method: 'POST',
      headers: {},
      url:    '',
      onComplete: function(data) {}
    });

    grunt.verbose.writeflags(options, 'Options');

    // Iterate over all specified file groups.
    this.files.forEach(function(f) {
      // there should be only 1 file in source, but just to make sure...
      var filepath = f.src[0];
      var field    = f.dest;

      // Warn on and remove invalid source files (if nonull was set).
      if (!grunt.file.exists(filepath)) {
        grunt.fail.warn('Source file "' + filepath + '" not found.');
        return false;
      }

      // get file size (necessary for multipart upload)
      fs.stat(filepath, function(err, stats) {
        if (err) {
          grunt.fail.warn('Error: ' + err);
          done(err);
        } else if (stats.isFile()) {
          var fileSize = stats.size;
          grunt.log.writeln('Uploading "' + filepath + '" as "' + field + '"');
          // build request data (because of dynamic key in data object)
          var reqData = options.data || {};
          reqData[field] = fs.createReadStream(filepath);
          // Build the HTTP request
          request({
            url:options.url,
            strictSSL: options.rejectUnauthorized,
            method: options.method,
            headers: options.headers,
            followRedirect: true,
            followAllRedirects: true,
            formData: reqData
          }, function optionalCallback(err, response, data) {
            if (err) {
              return grunt.fail.warn('Failed uploading (error code: ' + err + ')');
            }
            if (response !== null && response.statusCode >= 200 && response.statusCode < 300) {
              grunt.log.ok('Upload successful of "' + filepath + '" as "' + field + '" - ' + options.method + ' @ ' + options.url);
              options.onComplete(data);
            } else if (response !== null) {
              grunt.fail.warn('Failed uploading "' + filepath + '" as "' + field + '" (status code: ' + response.statusCode + ') - ' + options.method + ' @ ' + options.url);
            } else {
              grunt.fail.warn('Failed uploading "' + filepath + '" as "' + field + '" (status code: null) - ' + options.method + ' @ ' + options.url);
            }
            // callback once upload is done
            done(data);
          });
        }
      });
    });
  });
};
<|endoftext|>