<commit_before>/**
 * Tinytest for fictionaltime
 */
//quick simple time
const correctTime = {connectedToET: false, beginning: false, units: [100, 100, 1000], separators: [":", ":"], declaration: "SUT ", declarationLocation: "before"};
const ft = new FictionalTime(correctTime);

//full SUT time
const sut = {connectedToET: false, beginning: false, units: [500, 10, 100, 100, 1000], separators: [".", " ", ":", ":"], declaration: "SUT ", declarationLocation: "before"};
const sutTime = new FictionalTime(sut);

//time anchored to ET
const anchoredTime = {connectedToET: true, beginning: new Date('2020-01-01'), units: [500, 10, 100, 100, 1000], separators: [".", " ", ":", ":"], declaration: "SUT ET ", declarationLocation: "before"};
const anchorTime = new FictionalTime(anchoredTime);

//time with strange units
const strangeTime = {};
const strTime;

Tinytest.add('Succesfully create a fictionaltime constiable from object.', function (test) {
  const ftCorrect = new FictionalTime(correctTime);
  test.equal(typeof(ftCorrect) === "object", true);

  //#TODO:30 add more correct tests for all scenarios
  test.instanceOf(ftCorrect, FictionalTime);
});

Tinytest.add('Fail to create fictionaltime when incorrect object is passed in.', function(test) {
  const failingTime1 = "nothing";
  test.equal(new FictionalTime(failingTime1), {});

  const failingTime2 = 58;
  test.equal(new FictionalTime(failingTime2), {});

  const failingTime3 = false;
  test.equal(new FictionalTime(failingTime3), {});

  const failingTime4 = undefined;
  test.equal(new FictionalTime(failingTime4), {});

  const failingTime5 = {connectedToET: false, beginning: false, units: "none", separators: [":", ":"], declaration: "SUT", declarationLocation: "before"};
  test.equal(new FictionalTime(failingTime5), {});

  const failingTime6 = {connectedToET: false, beginning: false, units: [10, 100, 100], separators: false, declaration: "SUT", declarationLocation: "before"};
  test.equal(new FictionalTime(failingTime6), {});

  const failingTime7 = {connectedToET: "yes", beginning: false, units: [10, 100, 100], separators: [":", ":"], declaration: "SUT", declarationLocation: "before"};
  test.equal(new FictionalTime(failingTime7), {});

  const failingTime8 = {connectedToET: true, beginning: "today", units: [10, 100, 100], separators: [":", ":"], declaration: "SUT", declarationLocation: "before"};
  test.equal(new FictionalTime(failingTime8), {});

  const failingTime9 = {connectedToET: false, beginning: false, units: [10, 100, 100], separators: [":", ":"], declaration: "SUT", declarationLocation: false};
  test.equal(new FictionalTime(failingTime9), {});

  const failingTime10 = {connectedToET: false, beginning: false, units: [10, 100, 100], separators: [":", ":"], declaration: "SUT", declarationLocation: "never"};
  test.equal(new FictionalTime(failingTime10), {});

  const failingTime11 = {connectedToET: false, beginning: false, units: ["bing", "bang"], separators: [":", ":"], declaration: "SUT", declarationLocation: "before"};
  test.equal(new FictionalTime(failingTime11), {});

  const failingTime12 = {connectedToET: false, beginning: false, units: [10, 100, 100], separators: [10, 20], declaration: "SUT", declarationLocation: "before"};
  test.equal(new FictionalTime(failingTime12), {});

  const failingTime13 = {connectedToET: false, beginning: false, units: [10, 100, 100], separators: [10, 20], declaration: "SUT", declarationLocation: "both"};
  test.equal(new FictionalTime(failingTime13), {});
});

//
// Test for the different functions
// #TODO:60 add test for non SUT times
//
Tinytest.add('toTime()', function(test){
  test.equal(ft.toTime(99000), "0:00:99");
  test.equal(ft.toTime(100000), "0:01:00");
  test.equal(sutTime.toTime(100000), "0.000 0:01:00");
  //test.equal(ft.toTime(2000), "02");
});

Tinytest.add('toDate()', function(test){
  test.equal(sutTime.toDate(9999999000), "SUT 0.099 9:99:99");
  test.equal(sutTime.toDate(10000000000), "SUT 0.100 0:00:00");
  test.equal(sutTime.toDate(49900000000), "SUT 0.499 0:00:00");
  test.equal(sutTime.toDate(50000000000), "SUT 1.000 0:00:00");
  test.equal(sutTime.toDate(100000), "SUT 0.000 0:01:00");
});

Tinytest.add('toUnit()', function(test){
  test.equal(sutTime.toUnit(50000000000, 0), 1);
  test.equal(sutTime.toUnit(100000000000, 0), 2);
});

Tinytest.add('unitToMilliseconds()', function(test){
  test.equal(sutTime.unitToMilliseconds(1, 0), 50000000000);
});

Tinytest.add('currentTime()', function(test){
  test.equal(ft.currentDateTime(), false);
});
<commit_msg>Fix for Travis<commit_after>/**
 * Tinytest for fictionaltime
 */
//quick simple time
const correctTime = {connectedToET: false, beginning: false, units: [100, 100, 1000], separators: [":", ":"], declaration: "SUT ", declarationLocation: "before"};
const ft = new FictionalTime(correctTime);

//full SUT time
const sut = {connectedToET: false, beginning: false, units: [500, 10, 100, 100, 1000], separators: [".", " ", ":", ":"], declaration: "SUT ", declarationLocation: "before"};
const sutTime = new FictionalTime(sut);

//time anchored to ET
const anchoredTime = {connectedToET: true, beginning: new Date('2020-01-01'), units: [500, 10, 100, 100, 1000], separators: [".", " ", ":", ":"], declaration: "SUT ET ", declarationLocation: "before"};
const anchorTime = new FictionalTime(anchoredTime);

//time with strange units
const strangeTime = {};
const strTime = null;

Tinytest.add('Succesfully create a fictionaltime constiable from object.', function (test) {
  const ftCorrect = new FictionalTime(correctTime);
  test.equal(typeof(ftCorrect) === "object", true);

  //#TODO:30 add more correct tests for all scenarios
  test.instanceOf(ftCorrect, FictionalTime);
});

Tinytest.add('Fail to create fictionaltime when incorrect object is passed in.', function(test) {
  const failingTime1 = "nothing";
  test.equal(new FictionalTime(failingTime1), {});

  const failingTime2 = 58;
  test.equal(new FictionalTime(failingTime2), {});

  const failingTime3 = false;
  test.equal(new FictionalTime(failingTime3), {});

  const failingTime4 = undefined;
  test.equal(new FictionalTime(failingTime4), {});

  const failingTime5 = {connectedToET: false, beginning: false, units: "none", separators: [":", ":"], declaration: "SUT", declarationLocation: "before"};
  test.equal(new FictionalTime(failingTime5), {});

  const failingTime6 = {connectedToET: false, beginning: false, units: [10, 100, 100], separators: false, declaration: "SUT", declarationLocation: "before"};
  test.equal(new FictionalTime(failingTime6), {});

  const failingTime7 = {connectedToET: "yes", beginning: false, units: [10, 100, 100], separators: [":", ":"], declaration: "SUT", declarationLocation: "before"};
  test.equal(new FictionalTime(failingTime7), {});

  const failingTime8 = {connectedToET: true, beginning: "today", units: [10, 100, 100], separators: [":", ":"], declaration: "SUT", declarationLocation: "before"};
  test.equal(new FictionalTime(failingTime8), {});

  const failingTime9 = {connectedToET: false, beginning: false, units: [10, 100, 100], separators: [":", ":"], declaration: "SUT", declarationLocation: false};
  test.equal(new FictionalTime(failingTime9), {});

  const failingTime10 = {connectedToET: false, beginning: false, units: [10, 100, 100], separators: [":", ":"], declaration: "SUT", declarationLocation: "never"};
  test.equal(new FictionalTime(failingTime10), {});

  const failingTime11 = {connectedToET: false, beginning: false, units: ["bing", "bang"], separators: [":", ":"], declaration: "SUT", declarationLocation: "before"};
  test.equal(new FictionalTime(failingTime11), {});

  const failingTime12 = {connectedToET: false, beginning: false, units: [10, 100, 100], separators: [10, 20], declaration: "SUT", declarationLocation: "before"};
  test.equal(new FictionalTime(failingTime12), {});

  const failingTime13 = {connectedToET: false, beginning: false, units: [10, 100, 100], separators: [10, 20], declaration: "SUT", declarationLocation: "both"};
  test.equal(new FictionalTime(failingTime13), {});
});

//
// Test for the different functions
// #TODO:60 add test for non SUT times
//
Tinytest.add('toTime()', function(test){
  test.equal(ft.toTime(99000), "0:00:99");
  test.equal(ft.toTime(100000), "0:01:00");
  test.equal(sutTime.toTime(100000), "0.000 0:01:00");
  //test.equal(ft.toTime(2000), "02");
});

Tinytest.add('toDate()', function(test){
  test.equal(sutTime.toDate(9999999000), "SUT 0.099 9:99:99");
  test.equal(sutTime.toDate(10000000000), "SUT 0.100 0:00:00");
  test.equal(sutTime.toDate(49900000000), "SUT 0.499 0:00:00");
  test.equal(sutTime.toDate(50000000000), "SUT 1.000 0:00:00");
  test.equal(sutTime.toDate(100000), "SUT 0.000 0:01:00");
});

Tinytest.add('toUnit()', function(test){
  test.equal(sutTime.toUnit(50000000000, 0), 1);
  test.equal(sutTime.toUnit(100000000000, 0), 2);
});

Tinytest.add('unitToMilliseconds()', function(test){
  test.equal(sutTime.unitToMilliseconds(1, 0), 50000000000);
});

Tinytest.add('currentTime()', function(test){
  test.equal(ft.currentDateTime(), false);
});
<|endoftext|>


<commit_before>window.Perf = (function($, Perf) {
	$.extend(Perf, {
		reportUrl: 'stats/',
		revUrl: 'stats/rev.php',

		// should be defined before report or poll are run
		currentRev: undefined,

		report: function( data, after ) {
			$.extend(data, {
				pathname: location.pathname,
				agent: this.agent(),
				agentFull: window.navigator.userAgent,
				agentVersion: this.agentVersion()
			});

			$.post( this.reportUrl, data, after );
		},

		poll: function() {
			var self = this;

			setInterval(function() {
				$.get( self.revUrl, function( data ) {

					// if there's a new revision refresh or currentRev isn't being set
					if( self.currentRev && self.currentRev !== data ){
						location.href = location.href;
					}
				});
			}, 60000);
		},

		setCurrentRev: function() {
			var self = this;

			$.get( self.revUrl, function( data ) {
				self.currentRev = data;
			});
		},

		agent: function() {
			var agent = window.navigator.userAgent;

			for( name in this.agents ) {
				if( agent.indexOf( this.agents[name] ) > -1 ) {
					return this.agents[name];
				}
			}

			return agent;
		},

		agentVersion: function() {
			var agent = window.navigator.userAgent;

			agent.search(this.vRegexs[this.agent()] || "");

			return RegExp.$1 ? RegExp.$1 : "0.0";
		},

		agents: {
			ANDROID: "Android",
			WP: "Windows Phone OS",
			IPHONE: "iPhone OS",
			IPAD: "iPad; U; CPU OS",
			BLACKBERRY: "BlackBerry"
		},

		vRegexs: {}
	});

	Perf.vRegexs[Perf.agents.ANDROID] = /([0-9].[0-9].[0-9]);/;
	Perf.vRegexs[Perf.agents.WP] = /Windows Phone OS ([0-9].[0-9]);/;
	Perf.vRegexs[Perf.agents.IPHONE] = /iPhone OS ([0-9]_[0-9])/;
	Perf.vRegexs[Perf.agents.IPAD] = /iPad; U; CPU OS ([0-9]_[0-9])/;
	Perf.vRegexs[Perf.agents.BLACKBERRY] = /BlackBerry ([0-9]{4})/;

	return Perf;
})(jQuery, window.Perf || {});
<commit_msg>account for minor version only android version strings<commit_after>window.Perf = (function($, Perf) {
	$.extend(Perf, {
		reportUrl: 'stats/',
		revUrl: 'stats/rev.php',

		// should be defined before report or poll are run
		currentRev: undefined,

		report: function( data, after ) {
			$.extend(data, {
				pathname: location.pathname,
				agent: this.agent(),
				agentFull: window.navigator.userAgent,
				agentVersion: this.agentVersion()
			});

			$.post( this.reportUrl, data, after );
		},

		poll: function() {
			var self = this;

			setInterval(function() {
				$.get( self.revUrl, function( data ) {

					// if there's a new revision refresh or currentRev isn't being set
					if( self.currentRev && self.currentRev !== data ){
						location.href = location.href;
					}
				});
			}, 60000);
		},

		setCurrentRev: function() {
			var self = this;

			$.get( self.revUrl, function( data ) {
				self.currentRev = data;
			});
		},

		agent: function() {
			var agent = window.navigator.userAgent;

			for( name in this.agents ) {
				if( agent.indexOf( this.agents[name] ) > -1 ) {
					return this.agents[name];
				}
			}

			return agent;
		},

		agentVersion: function() {
			var agent = window.navigator.userAgent;

			agent.search(this.vRegexs[this.agent()] || "");

			return RegExp.$1 ? RegExp.$1 : "0.0";
		},

		agents: {
			ANDROID: "Android",
			WP: "Windows Phone OS",
			IPHONE: "iPhone OS",
			IPAD: "iPad; U; CPU OS",
			BLACKBERRY: "BlackBerry"
		},

		vRegexs: {}
	});

	Perf.vRegexs[Perf.agents.ANDROID] = /([0-9].[0-9])(.[0-9])?;/;
	Perf.vRegexs[Perf.agents.WP] = /Windows Phone OS ([0-9].[0-9]);/;
	Perf.vRegexs[Perf.agents.IPHONE] = /iPhone OS ([0-9]_[0-9])/;
	Perf.vRegexs[Perf.agents.IPAD] = /iPad; U; CPU OS ([0-9]_[0-9])/;
	Perf.vRegexs[Perf.agents.BLACKBERRY] = /BlackBerry ([0-9]{4})/;

	return Perf;
})(jQuery, window.Perf || {});
<|endoftext|>


<commit_before>/*global System, declare, it, xit, describe, beforeEach, afterEach, before, after*/
import { StyleSheet } from "../style-sheets.js";
import { Morph, MorphicEnv, HorizontalLayout, morph } from "../index.js";
import { expect } from "mocha-es6";
import { pt, rect, Color, Rectangle } from "lively.graphics";
import { Sizzle, StylingVisitor } from "../sizzle.js";
import { createDOMEnvironment } from "../rendering/dom-helper.js";

describe("Sizzle", () => {
  it("does not confuse AND-classes with structural class relation", () => {
    const m = new Morph({styleClasses: ["root", "child"]});
    expect(new Sizzle(m).select('.root .child')).to.be.empty;
  });
});

var env, world;

function createDummyWorld() {
  world = morph({type: 'world', name: "world"});
  return world;
}

describe("Style Rules", function() {

  if (System.get("@system-env").node)
    this.timeout(10000);

  beforeEach(async () => env = await MorphicEnv.pushDefault(new MorphicEnv(await createDOMEnvironment())).setWorld(createDummyWorld()));
  afterEach(() =>  MorphicEnv.popDefault().uninstall());

  it("applies a style to a morph", () => {
    const m1 = new Morph({
      styleClasses: ["root"],
      submorphs: [
        {
          name: 'B',
          styleClasses: ["bob"],
          submorphs: [
            {
              name: "A",
              styleClasses: ["alice"]
            }
          ]
        }
      ]
    }),
    m2 = m1.get('A'),
    m3 = m1.get('B');
    m1.styleSheets = new StyleSheet({
      ".root": {fill: Color.orange},
      ".bob": {fill: Color.red},
      ".alice": {fill: Color.yellow}
    });

    new StylingVisitor(m1).visit();

    expect(m1.fill).equals(Color.orange);
    expect(m2.fill).equals(Color.yellow);
    expect(m3.fill).equals(Color.red);
  });

  it("applies a style once a morph is added to the hierarchy", () => {
    const m1 = new Morph({styleClasses: ["root"]}), m2 = new Morph({styleClasses: ["child"]});
    m1.styleSheets = new StyleSheet({
      ".root": {fill: Color.orange},
      ".child": {fill: Color.green}
    });
    m1.addMorph(m2);
    new StylingVisitor(m1).visit();
    expect(m2.fill).equals(Color.green);
    m2.submorphs = [
      {
        styleClasses: ["child"],
        submorphs: [{styleClasses: ["child"]}]
      },
      {styleClasses: ["child"]},
      {styleClasses: ["root"]}
    ];
    new StylingVisitor(m1).visit();
    expect(m2.submorphs.map(m => m.fill)).equals([Color.green, Color.green, Color.orange]);
    expect(m2.submorphs[0].submorphs[0].fill).equals(Color.green);
  });

  it("updates the style once a morph changes morphClass", () => {
    const m1 = new Morph({styleClasses: ["root"]}), m2 = new Morph({styleClasses: ["child"]});
    m1.styleSheets = new StyleSheet({
      ".root": {fill: Color.orange},
      ".child": {fill: Color.green}
    });
    m1.addMorph(m2);
    m1.styleClasses = ["child"];
    m2.styleClasses = ["root"];
    new StylingVisitor(m1).visit();
    expect(m1.fill).equals(Color.green);
    expect(m2.fill).equals(Color.orange);
  });

  it("updates the style once a morph changes name", () => {
    const m1 = new Morph({styleClasses: ["root"]}), m2 = new Morph({styleClasses: ["child"]});
    m1.styleSheets = new StyleSheet({
      "[name=root]": {fill: Color.orange},
      "[name=child]": {fill: Color.green}
    });
    m1.addMorph(m2);
    m1.name = "child";
    m2.name = "root";
    new StylingVisitor(m1).visit();
    expect(m1.fill).equals(Color.green);
    expect(m2.fill).equals(Color.orange);
  });

  it("updates the style once a morph is added", () => {
    const m0 = new Morph(),
          m1 = new Morph({styleClasses: ["root"]}),
          m2 = new Morph({styleClasses: ["child"]}),
          m3 = new Morph({styleClasses: ["leaf"]});
    m0.styleSheets = new StyleSheet({
      ".root": {fill: Color.orange},
      ".child": {fill: Color.brown},
      ".leaf": {fill: Color.green}
    });
    m1.addMorph(m2);
    m2.addMorph(m3);
    new StylingVisitor(m0).visit();
    expect(m2.fill).not.equals(Color.brown);
    expect(m3.fill).not.equals(Color.green);
    m0.addMorph(m1);
    new StylingVisitor(m0).visit();
    expect(m1.fill).equals(Color.orange);
    expect(m2.fill).equals(Color.brown);
    expect(m3.fill).equals(Color.green);
  });

  it("can be nested", () => {
    const m1 = new Morph({name: "m1", styleClasses: ["root"]}),
          m2 = new Morph({name: "m2", styleClasses: ["child"]}),
          m3 = new Morph({name: "m3", styleClasses: ["child"]});
    m2.styleSheets = new StyleSheet({".child": {fill: Color.black, borderColor: Color.red}});
    m1.styleSheets = new StyleSheet({
      ".root": {fill: Color.orange},
      ".child": {fill: Color.green}
    });
    m2.addMorph(m3);
    m1.addMorph(m2);
    let visitor = new StylingVisitor(m1);
    visitor.visit();
    expect(visitor.expressionCache).to.have.all.keys(m1.id, m2.id);
    expect(visitor.expressionCache[m1.id]).to.have.all.keys('.child', '.root');
    expect(visitor.expressionCache[m2.id]).to.have.all.keys('.child');
    expect(m1.fill).equals(Color.orange);
    expect(m2.fill).equals(Color.black);
    expect(m3.fill).equals(Color.black);
    expect(m3.borderColor.left).equals(Color.red);
  });

  it("handles multiple style sheets per morph", () => {
    const m1 = new Morph({name: "m1", styleClasses: ["root"]}),
          m2 = new Morph({name: "m2", styleClasses: ["child"]}),
          m3 = new Morph({name: "m3", styleClasses: ["child"]});
    m1.styleSheets = [
      new StyleSheet({
        ".root": {fill: Color.orange},
        ".child": {fill: Color.green, borderWidth: 10}
      }),
      new StyleSheet({".child": {fill: Color.black, borderColor: Color.red}})
    ];
    m2.addMorph(m3);
    m1.addMorph(m2);
    let visitor = new StylingVisitor(m1);
    visitor.visit();
    expect(m2.borderWidth.valueOf()).equals(10);
    expect(m2.borderColor.valueOf()).equals(Color.red);
    expect(m2.fill).equals(Color.black);
  });

  it("updates layouts on changing submorphs", async () => {
    const m1 = new Morph({name: "m1", styleClasses: ["root"]}),
          m2 = new Morph({name: "m2", styleClasses: ["child"]}),
          m3 = new Morph({name: "m3", styleClasses: ["child"]});
    m1.styleSheets = new StyleSheet({
      ".root": {layout: new HorizontalLayout({resizeContainer: true})},
      ".child": {fill: Color.green},
      ".new": {extent: pt(100, 0)}
    });
    m1.addMorph(m3);
    m1.addMorph(m2);
    world.addMorph(m1);
    await m1.whenRendered();
    expect(m1.width).equals(m2.width + m3.width);
    m2.styleClasses = ["new"];
    await world.whenRendered();
    expect(m2.width).equals(100);
    expect(m1.width).equals(100 + m3.width);
  });

  it("results in the same transform behaviors as if morphs where directly set", () => {
    let hierarchy = new Morph({
      styleSheets: [
        new StyleSheet({
          ".root": {position: pt(20, 20)},
          ".bob": {position: pt(40, 40)},
          ".alice": {extent: pt(30, 30)}
        })
      ],
      styleClasses: ["root"],
      submorphs: [
        {
          styleClasses: ["bob"],
          submorphs: [
            {
              name: "A",
              styleClasses: ["alice"]
            }
          ]
        }
      ]
    });
    new StylingVisitor(hierarchy).visit();
    expect(hierarchy.get("A").extent).equals(pt(30, 30));
    expect(hierarchy.get("A").globalBounds()).equals(rect(60, 60, 30, 30));
  });

  it('updates all affected morphs if style sheet rules are changed', () => {
    var sheet, hierarchy = new Morph({
      styleSheets: [
        sheet = new StyleSheet({
          ".root": {position: pt(20, 20)},
          ".bob": {position: pt(40, 40)},
          ".alice": {extent: pt(30, 30)}
        })
      ],
      styleClasses: ["root"],
      submorphs: [
        {
          name: 'B',
          styleClasses: ["bob"],
          submorphs: [
            {
              name: "A",
              styleClasses: ["alice"]
            }
          ]
        }
      ]
    });
    let v = new StylingVisitor(hierarchy);
    v.visit();
    sheet.setRule('.bob', {fill: Color.red});
    sheet.removeRule('.alice');
    v.visit()

    expect(hierarchy.get('A').position).equals(pt(0,0));
    expect(hierarchy.get('B').position).equals(pt(0,0));
    expect(hierarchy.get('B').fill).equals(Color.red);
  });

  it("resets properties to their initial values, if rules stop being applied", () => {
    var sheet, hierarchy = new Morph({
      styleSheets: [
        sheet = new StyleSheet({
          ".root": {position: pt(20, 20)},
          ".bob": {position: pt(40, 40)},
          ".alice": {extent: pt(30, 30)}
        })
      ],
      styleClasses: ["root"],
      submorphs: [
        {
          name: 'B',
          styleClasses: ["bob"],
          submorphs: [
            {
              name: "A",
              styleClasses: ["alice"]
            }
          ]
        }
      ]
    });

    expect(hierarchy.get('A').extent).equals(pt(10,10))

    let v = new StylingVisitor(hierarchy);
    v.visit();

    expect(hierarchy.get('A').extent).equals(pt(30,30))
    // the StylingVisitor remembers the overridden values for each morph
    hierarchy.get('A').removeStyleClass('alice');
    v.visit();

    expect(hierarchy.get('A').extent).equals(pt(10,10))
  })
});
<commit_msg>sizzle: add regression test for matching bug<commit_after>/*global System, declare, it, xit, describe, beforeEach, afterEach, before, after*/
import { StyleSheet } from "../style-sheets.js";
import { Morph, MorphicEnv, HorizontalLayout, morph } from "../index.js";
import { expect } from "mocha-es6";
import { pt, rect, Color, Rectangle } from "lively.graphics";
import { Sizzle, StylingVisitor, SizzleVisitor } from "../sizzle.js";
import { createDOMEnvironment } from "../rendering/dom-helper.js";

class TestVisitor extends SizzleVisitor {

  constructor(context, expr) {
    super(context);
    this.sizzleExpression = expr;
  }

  retrieveExpressions(morph) {
    // only use the assigned expression for the whole hierarchy
    return morph.owner ? false : {[this.sizzleExpression]: true};
  }

  visitMorph(morph, matching) {
    morph.matched = matching.length > 0;
  }

  getChildren(morph) {
    return morph.submorphs;
  }
}

describe("Sizzle", () => {

  it("does not confuse AND-classes with structural class relation", () => {
    const m = new Morph({styleClasses: ["root", "child"]});
    expect(new Sizzle(m).select('.root .child')).to.be.empty;
  });

  it("matches nested morphs correctly", () => {
    const m = new Morph({
      styleClasses: ["root", "child"],
      submorphs: [
        {
          type: 'label',
          submorphs: [{name: 'L', type: "label", matched: false}]
        }
      ]
    });
    new TestVisitor(m, '.root.child .Label').visit()
    expect(m.get('L').matched).to.be.true;
    expect(new Sizzle(m).select('.root.child .Label')).to.not.be.empty;
  });
});

var env, world;

function createDummyWorld() {
  world = morph({type: 'world', name: "world"});
  return world;
}

describe("Style Rules", function() {

  if (System.get("@system-env").node)
    this.timeout(10000);

  beforeEach(async () => env = await MorphicEnv.pushDefault(new MorphicEnv(await createDOMEnvironment())).setWorld(createDummyWorld()));
  afterEach(() =>  MorphicEnv.popDefault().uninstall());

  it("applies a style to a morph", () => {
    const m1 = new Morph({
      styleClasses: ["root"],
      submorphs: [
        {
          name: 'B',
          styleClasses: ["bob"],
          submorphs: [
            {
              name: "A",
              styleClasses: ["alice"]
            }
          ]
        }
      ]
    }),
    m2 = m1.get('A'),
    m3 = m1.get('B');
    m1.styleSheets = new StyleSheet({
      ".root": {fill: Color.orange},
      ".bob": {fill: Color.red},
      ".alice": {fill: Color.yellow}
    });

    new StylingVisitor(m1).visit();

    expect(m1.fill).equals(Color.orange);
    expect(m2.fill).equals(Color.yellow);
    expect(m3.fill).equals(Color.red);
  });

  it("applies a style once a morph is added to the hierarchy", () => {
    const m1 = new Morph({styleClasses: ["root"]}), m2 = new Morph({styleClasses: ["child"]});
    m1.styleSheets = new StyleSheet({
      ".root": {fill: Color.orange},
      ".child": {fill: Color.green}
    });
    m1.addMorph(m2);
    new StylingVisitor(m1).visit();
    expect(m2.fill).equals(Color.green);
    m2.submorphs = [
      {
        styleClasses: ["child"],
        submorphs: [{styleClasses: ["child"]}]
      },
      {styleClasses: ["child"]},
      {styleClasses: ["root"]}
    ];
    new StylingVisitor(m1).visit();
    expect(m2.submorphs.map(m => m.fill)).equals([Color.green, Color.green, Color.orange]);
    expect(m2.submorphs[0].submorphs[0].fill).equals(Color.green);
  });

  it("updates the style once a morph changes morphClass", () => {
    const m1 = new Morph({styleClasses: ["root"]}), m2 = new Morph({styleClasses: ["child"]});
    m1.styleSheets = new StyleSheet({
      ".root": {fill: Color.orange},
      ".child": {fill: Color.green}
    });
    m1.addMorph(m2);
    m1.styleClasses = ["child"];
    m2.styleClasses = ["root"];
    new StylingVisitor(m1).visit();
    expect(m1.fill).equals(Color.green);
    expect(m2.fill).equals(Color.orange);
  });

  it("updates the style once a morph changes name", () => {
    const m1 = new Morph({styleClasses: ["root"]}), m2 = new Morph({styleClasses: ["child"]});
    m1.styleSheets = new StyleSheet({
      "[name=root]": {fill: Color.orange},
      "[name=child]": {fill: Color.green}
    });
    m1.addMorph(m2);
    m1.name = "child";
    m2.name = "root";
    new StylingVisitor(m1).visit();
    expect(m1.fill).equals(Color.green);
    expect(m2.fill).equals(Color.orange);
  });

  it("updates the style once a morph is added", () => {
    const m0 = new Morph(),
          m1 = new Morph({styleClasses: ["root"]}),
          m2 = new Morph({styleClasses: ["child"]}),
          m3 = new Morph({styleClasses: ["leaf"]});
    m0.styleSheets = new StyleSheet({
      ".root": {fill: Color.orange},
      ".child": {fill: Color.brown},
      ".leaf": {fill: Color.green}
    });
    m1.addMorph(m2);
    m2.addMorph(m3);
    new StylingVisitor(m0).visit();
    expect(m2.fill).not.equals(Color.brown);
    expect(m3.fill).not.equals(Color.green);
    m0.addMorph(m1);
    new StylingVisitor(m0).visit();
    expect(m1.fill).equals(Color.orange);
    expect(m2.fill).equals(Color.brown);
    expect(m3.fill).equals(Color.green);
  });

  it("can be nested", () => {
    const m1 = new Morph({name: "m1", styleClasses: ["root"]}),
          m2 = new Morph({name: "m2", styleClasses: ["child"]}),
          m3 = new Morph({name: "m3", styleClasses: ["child"]});
    m2.styleSheets = new StyleSheet({".child": {fill: Color.black, borderColor: Color.red}});
    m1.styleSheets = new StyleSheet({
      ".root": {fill: Color.orange},
      ".child": {fill: Color.green}
    });
    m2.addMorph(m3);
    m1.addMorph(m2);
    let visitor = new StylingVisitor(m1);
    visitor.visit();
    expect(visitor.expressionCache).to.have.all.keys(m1.id, m2.id);
    expect(visitor.expressionCache[m1.id]).to.have.all.keys('.child', '.root');
    expect(visitor.expressionCache[m2.id]).to.have.all.keys('.child');
    expect(m1.fill).equals(Color.orange);
    expect(m2.fill).equals(Color.black);
    expect(m3.fill).equals(Color.black);
    expect(m3.borderColor.left).equals(Color.red);
  });

  it("handles multiple style sheets per morph", () => {
    const m1 = new Morph({name: "m1", styleClasses: ["root"]}),
          m2 = new Morph({name: "m2", styleClasses: ["child"]}),
          m3 = new Morph({name: "m3", styleClasses: ["child"]});
    m1.styleSheets = [
      new StyleSheet({
        ".root": {fill: Color.orange},
        ".child": {fill: Color.green, borderWidth: 10}
      }),
      new StyleSheet({".child": {fill: Color.black, borderColor: Color.red}})
    ];
    m2.addMorph(m3);
    m1.addMorph(m2);
    let visitor = new StylingVisitor(m1);
    visitor.visit();
    expect(m2.borderWidth.valueOf()).equals(10);
    expect(m2.borderColor.valueOf()).equals(Color.red);
    expect(m2.fill).equals(Color.black);
  });

  it("updates layouts on changing submorphs", async () => {
    const m1 = new Morph({name: "m1", styleClasses: ["root"]}),
          m2 = new Morph({name: "m2", styleClasses: ["child"]}),
          m3 = new Morph({name: "m3", styleClasses: ["child"]});
    m1.styleSheets = new StyleSheet({
      ".root": {layout: new HorizontalLayout({resizeContainer: true})},
      ".child": {fill: Color.green},
      ".new": {extent: pt(100, 0)}
    });
    m1.addMorph(m3);
    m1.addMorph(m2);
    world.addMorph(m1);
    await m1.whenRendered();
    expect(m1.width).equals(m2.width + m3.width);
    m2.styleClasses = ["new"];
    await world.whenRendered();
    expect(m2.width).equals(100);
    expect(m1.width).equals(100 + m3.width);
  });

  it("results in the same transform behaviors as if morphs where directly set", () => {
    let hierarchy = new Morph({
      styleSheets: [
        new StyleSheet({
          ".root": {position: pt(20, 20)},
          ".bob": {position: pt(40, 40)},
          ".alice": {extent: pt(30, 30)}
        })
      ],
      styleClasses: ["root"],
      submorphs: [
        {
          styleClasses: ["bob"],
          submorphs: [
            {
              name: "A",
              styleClasses: ["alice"]
            }
          ]
        }
      ]
    });
    new StylingVisitor(hierarchy).visit();
    expect(hierarchy.get("A").extent).equals(pt(30, 30));
    expect(hierarchy.get("A").globalBounds()).equals(rect(60, 60, 30, 30));
  });

  it('updates all affected morphs if style sheet rules are changed', () => {
    var sheet, hierarchy = new Morph({
      styleSheets: [
        sheet = new StyleSheet({
          ".root": {position: pt(20, 20)},
          ".bob": {position: pt(40, 40)},
          ".alice": {extent: pt(30, 30)}
        })
      ],
      styleClasses: ["root"],
      submorphs: [
        {
          name: 'B',
          styleClasses: ["bob"],
          submorphs: [
            {
              name: "A",
              styleClasses: ["alice"]
            }
          ]
        }
      ]
    });
    let v = new StylingVisitor(hierarchy);
    v.visit();
    sheet.setRule('.bob', {fill: Color.red});
    sheet.removeRule('.alice');
    v.visit()

    expect(hierarchy.get('A').position).equals(pt(0,0));
    expect(hierarchy.get('B').position).equals(pt(0,0));
    expect(hierarchy.get('B').fill).equals(Color.red);
  });

  it("resets properties to their initial values, if rules stop being applied", () => {
    var sheet, hierarchy = new Morph({
      styleSheets: [
        sheet = new StyleSheet({
          ".root": {position: pt(20, 20)},
          ".bob": {position: pt(40, 40)},
          ".alice": {extent: pt(30, 30)}
        })
      ],
      styleClasses: ["root"],
      submorphs: [
        {
          name: 'B',
          styleClasses: ["bob"],
          submorphs: [
            {
              name: "A",
              styleClasses: ["alice"]
            }
          ]
        }
      ]
    });

    expect(hierarchy.get('A').extent).equals(pt(10,10))

    let v = new StylingVisitor(hierarchy);
    v.visit();

    expect(hierarchy.get('A').extent).equals(pt(30,30))
    // the StylingVisitor remembers the overridden values for each morph
    hierarchy.get('A').removeStyleClass('alice');
    v.visit();

    expect(hierarchy.get('A').extent).equals(pt(10,10))
  })

});
<|endoftext|>


<commit_before>var _ga = _ga || {}; //ensure that _ga has been initialized
var _gaq = _gaq || [];

/*
 * Define a function on the _ga object that will automatically push events to all the registered trackers
 * It is possible that there is more than one tracker, hence this function
 */

_ga.getEventTrackers_ = function(category, action, opt_label) {

    //console.log("cat: " + category + "; act: " + action + "; opt_l: " + opt_label);
    // we can return this anonnymous function and pass it to the _gaq
    return function() {
        var trackers = _gat._getTrackers(); //Gets an array of allt he trackers from the _gat object
        for (var i = 0, tracker; tracker = trackers[i]; i++) {

            //Now we have a handle to a tracker, we can send the event to GA
            //The tracker returns a boolean true if the event was successfully tracked, false otherwise
            var result = tracker._trackEvent(category, action, opt_label, 1);

            //for debugging the event tracking we can check the return value
            /*if(result) {
                console.log("Tracked " + category + " event " + action + " " + opt_label + " successfully"); //log the event to the console.
            } else {
                console.log("Tracking " + category + " event " + action + " " + opt_label +  " FAILED"); //log the event to the console.
            }*/
        }
    };
};

function trackCheckedFilters()  {

    // get all checked checkboxes
    $('#filter_block [type="checkbox"]:checked').each(function () {
        _gaq.push(_ga.getEventTrackers_("Filters", window.default_location+"-"+this.name, this.value));
    });

    // get all checked radio buttons
    $('#filter_block [type="radio"]:checked').each(function () {
        _gaq.push(_ga.getEventTrackers_("Filters", window.default_location+"-"+this.name, this.value));
    });

    // TODO: get all selected dropdowns items
    /*$('#filter_block option:selected').each(function () {
        _gaq.push(_ga.getEventTrackers_("Filters", window.default_location+"-"+this.name, this.value));
    })*/

}
<commit_msg>Set analytics up to report on selects in the filters, with some unfortunate special logic around buildings.<commit_after>var _ga = _ga || {}; //ensure that _ga has been initialized
var _gaq = _gaq || [];

/*
 * Define a function on the _ga object that will automatically push events to all the registered trackers
 * It is possible that there is more than one tracker, hence this function
 */

_ga.getEventTrackers_ = function(category, action, opt_label) {

    //console.log("cat: " + category + "; act: " + action + "; opt_l: " + opt_label);
    //console.log(action + "; " + opt_label);
    // we can return this anonnymous function and pass it to the _gaq
    return function() {
        var trackers = _gat._getTrackers(); //Gets an array of allt he trackers from the _gat object
        for (var i = 0, tracker; tracker = trackers[i]; i++) {

            //Now we have a handle to a tracker, we can send the event to GA
            //The tracker returns a boolean true if the event was successfully tracked, false otherwise
            var result = tracker._trackEvent(category, action, opt_label, 1);

            //for debugging the event tracking we can check the return value
            /*if(result) {
                console.log("Tracked " + category + " event " + action + " " + opt_label + " successfully"); //log the event to the console.
            } else {
                console.log("Tracking " + category + " event " + action + " " + opt_label +  " FAILED"); //log the event to the console.
            }*/
        }
    };
};

function trackCheckedFilters()  {

    // get all checked checkboxes
    $('#filter_block [type="checkbox"]:checked').each(function () {
        _gaq.push(_ga.getEventTrackers_("Filters", window.default_location+"-"+this.name, this.value));
    });

    // get all checked radio buttons
    $('#filter_block [type="radio"]:checked').each(function () {
        _gaq.push(_ga.getEventTrackers_("Filters", window.default_location+"-"+this.name, this.value));
    });

    // TODO: get all selected dropdowns items
    $('#filter_block option:selected').each(function () {
        // TODO: This next bit is not as awesome as I would like it, basically we're assuming anything with a label attribute is a building - would be nice if that was more robust.
        if (this.parentNode.hasOwnProperty('label')) {
            _gaq.push(_ga.getEventTrackers_("Filters", window.default_location+"-building", this.value));
        } else {
            _gaq.push(_ga.getEventTrackers_("Filters", window.default_location+"-"+this.parentNode.id, this.value));
        }
    })

}
<|endoftext|>


<commit_before>var tick = function() {};

(function() {
	var editor = ace.edit("editor");
	editor.setTheme("ace/theme/monokai");
	editor.getSession().setMode("ace/mode/javascript");
	editor.setValue([
		'tick = function (submarine) {',
		'\t/*',
		'\tWrite your code in here',
		'\t',
		'\tFunctions available:',
		'\t\tsubmarine.moveForward()',
		'\t\tsubmarine.moveBackwards()',
		'\t\tsubmarine.rotate(degrees)',
		'\t\t\tMax rotation: +-5',
		'\t\tsubmarine.getInfo()',
		'\t\t\tReturns: { x, y, rotation, energy, minX, minY,',
		'\t\t\t\tmaxX, maxY, maxEnergy, collision }',
		'\t\tsubmarine.sonar()',
		'\t\t\tReturns: { distanceToEnemy, incline }',
		'\t*/',
		'\t',
		'\t',
		'};'
	].join('\n'));
	editor.gotoLine(16);

	var windowHeight;

	var $statusBar;
	var socket;
	var isPlaying = false;
	var updater;

	var submarine, enemySubmarine;

	$(document).ready(function() {
		windowHeight = $(window).height();
		$statusBar = $('#statusText');
		resize();

		bindEvents();
		initSocketIO();
	});

	function resize() {
		$('#editor-container').height(windowHeight - 70);
		$('#canvas-container').height(windowHeight - 70);
	}

	function bindEvents() {
		$('#playBtn').click(play);
	}

	function setStatus(status) {
		$statusBar.text(status);
	}

	function initSocketIO() {
		socket = io('//localhost' + location.port ? ':' + location.port : '');
		socket.on('start', startGame);
		socket.on('update', updateEnemy);
		socket.on('disconnect', disconnected);
		socket.on('enemyleft', enemyLeft);
	}

	function enemyLeft() {
		isPlaying = false;
		setStatus('The enemy has left the game!');
	}

	function disconnected() {
		isPlaying = false;
		setStatus('You have been disconnected from the server!');
	}

	function updateEnemy(data) {
		enemySubmarine.update(data);
	}

	function startGame(data) {
		setStatus('Game started!');

		isPlaying = true;

		enemySubmarine = submarineFactory(data.player === 1 ? 2 : 1, true);
		submarine = submarineFactory(data.player, false, enemySubmarine);

		if(updater) clearInterval(updater);

		updater = setInterval(function() {
			if(!isPlaying) return;

			tick(submarine);
			update();
			paint();
		}, 50);
	}

	function play() {
		var code = editor.getValue();

		try {
			eval(code);
		}
		catch(e) {
			return setStatus('There is an error on your code. please fix');
		}

		if(!isPlaying) {
			socket.emit('queue');
			setStatus('In queue...');
		}
	}

	var canvas = document.getElementById("canvas");
	var ctx = canvas.getContext("2d");
	var subImgs = [
		document.getElementById("subImg"),
		document.getElementById("subImg2")
	];

	var lastStatus;

	function update() {
		var currentStatus = submarine.getInfo();

		if(!_.isEqual(currentStatus, lastStatus)) {
			socket.emit('update', {
				x: currentStatus.x,
				y: currentStatus.y,
				rotation: currentStatus.rotation,
				energy: currentStatus.energy
			});
		}

		lastStatus = currentStatus;
	}

	function paint() {
		ctx.beginPath();
		ctx.rect(0, 0, 600, 400);
		ctx.fillStyle = '#103669';
		ctx.fill();

		var info = submarine.getInfo();
		paintEnergyBar(info, 20, 20, 100, 20);

		ctx.beginPath();
		ctx.moveTo(info.x + 18, info.y - 14);
		ctx.lineTo(info.x + 32, info.y - 14);
		ctx.lineTo(info.x + 25, info.y - 3);
		ctx.closePath();
		ctx.fillStyle = "#500";
		ctx.fill();

		paintSubmarine(info);

		info = enemySubmarine.getInfo();
		paintSubmarine(info);
		paintEnergyBar(info, info.x + 5, info.y - 14, 40, 5);
	}

	function paintEnergyBar(info, x, y, w, h) {
		ctx.beginPath();
		ctx.rect(x, y, w, h);
		ctx.fillStyle = '#500';
		ctx.fill();
		ctx.beginPath();
		ctx.rect(x, y, info.energy * w / info.maxEnergy, h);
		ctx.fillStyle = '#050';
		ctx.fill();
	}

	function paintSubmarine(info) {
		ctx.translate(info.x + 25, info.y + 25);
		ctx.rotate(Math.PI / 180 * info.rotation);
		ctx.drawImage(subImgs[info.player === 1 ? 0 : 1] , -25, -25);
		ctx.setTransform(1, 0, 0, 1, 0, 0);
	}
})();
<commit_msg>changed status message<commit_after>var tick = function() {};

(function() {
	var editor = ace.edit("editor");
	editor.setTheme("ace/theme/monokai");
	editor.getSession().setMode("ace/mode/javascript");
	editor.setValue([
		'tick = function (submarine) {',
		'\t/*',
		'\tWrite your code in here',
		'\t',
		'\tFunctions available:',
		'\t\tsubmarine.moveForward()',
		'\t\tsubmarine.moveBackwards()',
		'\t\tsubmarine.rotate(degrees)',
		'\t\t\tMax rotation: +-5',
		'\t\tsubmarine.getInfo()',
		'\t\t\tReturns: { x, y, rotation, energy, minX, minY,',
		'\t\t\t\tmaxX, maxY, maxEnergy, collision }',
		'\t\tsubmarine.sonar()',
		'\t\t\tReturns: { distanceToEnemy, incline }',
		'\t*/',
		'\t',
		'\t',
		'};'
	].join('\n'));
	editor.gotoLine(16);

	var windowHeight;

	var $statusBar;
	var socket;
	var isPlaying = false;
	var updater;

	var submarine, enemySubmarine;

	$(document).ready(function() {
		windowHeight = $(window).height();
		$statusBar = $('#statusText');

		resize();

		bindEvents();
		initSocketIO();
	});

	function resize() {
		$('#editor-container').height(windowHeight - 70);
		$('#canvas-container').height(windowHeight - 70);
	}

	function bindEvents() {
		$('#playBtn').click(play);
	}

	function setStatus(status) {
		$statusBar.text(status);
	}

	function initSocketIO() {
		socket = io('//localhost' + location.port ? ':' + location.port : '');
		socket.on('start', startGame);
		socket.on('update', updateEnemy);
		socket.on('disconnect', disconnected);
		socket.on('enemyleft', enemyLeft);
	}

	function enemyLeft() {
		isPlaying = false;
		setStatus('The enemy has left the game!');
	}

	function disconnected() {
		isPlaying = false;
		setStatus('You have been disconnected from the server!');
	}

	function updateEnemy(data) {
		enemySubmarine.update(data);
	}

	function startGame(data) {
		setStatus('Game started!');

		isPlaying = true;

		enemySubmarine = submarineFactory(data.player === 1 ? 2 : 1, true);
		submarine = submarineFactory(data.player, false, enemySubmarine);

		if(updater) clearInterval(updater);

		updater = setInterval(function() {
			if(!isPlaying) return;

			tick(submarine);
			update();
			paint();
		}, 50);
	}

	function play() {
		var code = editor.getValue();

		try {
			eval(code);
		}
		catch(e) {
			return setStatus('There is an error on your code. please fix');
		}

		if(!isPlaying) {
			socket.emit('queue');
			setStatus('Finding match...');
		}
	}

	var canvas = document.getElementById("canvas");
	var ctx = canvas.getContext("2d");
	var subImgs = [
		document.getElementById("subImg"),
		document.getElementById("subImg2")
	];

	var lastStatus;

	function update() {
		var currentStatus = submarine.getInfo();

		if(!_.isEqual(currentStatus, lastStatus)) {
			socket.emit('update', {
				x: currentStatus.x,
				y: currentStatus.y,
				rotation: currentStatus.rotation,
				energy: currentStatus.energy
			});
		}

		lastStatus = currentStatus;
	}

	function paint() {
		ctx.beginPath();
		ctx.rect(0, 0, 600, 400);
		ctx.fillStyle = '#103669';
		ctx.fill();

		var info = submarine.getInfo();
		paintEnergyBar(info, 20, 20, 100, 20);

		ctx.beginPath();
		ctx.moveTo(info.x + 18, info.y - 14);
		ctx.lineTo(info.x + 32, info.y - 14);
		ctx.lineTo(info.x + 25, info.y - 3);
		ctx.closePath();
		ctx.fillStyle = "#500";
		ctx.fill();

		paintSubmarine(info);

		info = enemySubmarine.getInfo();
		paintSubmarine(info);
		paintEnergyBar(info, info.x + 5, info.y - 14, 40, 5);
	}

	function paintEnergyBar(info, x, y, w, h) {
		ctx.beginPath();
		ctx.rect(x, y, w, h);
		ctx.fillStyle = '#500';
		ctx.fill();
		ctx.beginPath();
		ctx.rect(x, y, info.energy * w / info.maxEnergy, h);
		ctx.fillStyle = '#050';
		ctx.fill();
	}

	function paintSubmarine(info) {
		ctx.translate(info.x + 25, info.y + 25);
		ctx.rotate(Math.PI / 180 * info.rotation);
		ctx.drawImage(subImgs[info.player === 1 ? 0 : 1] , -25, -25);
		ctx.setTransform(1, 0, 0, 1, 0, 0);
	}
})();
<|endoftext|>


<commit_before>function calculateDistanceHaversine(location1, location2, radius) {

	radius = radius || 6371e3;

	// convert latitude and longitude to radians first
    var lat1 = Math.PI * location1.latitude / 180;
    var lat2 = Math.PI * location2.latitude / 180;
    var d_phi = lat2 - lat1;
    var d_lambda = Math.PI * (location2.longitude - location1.longitude) / 180;

    var a = Math.sin(d_phi / 2) * Math.sin(d_phi / 2) + Math.cos(lat1) * Math.cos(lat2) * Math.sin(d_lambda / 2) * Math.sin(d_lambda / 2);
    var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));

    // console.log('|| {} - {} ||= {}'.format(location2, location1, earth_radius * c))

    return radius * c;
}

function breakSegments(data, byStatus, separationRadius, timeInterval) {

	separationRadius = separationRadius || [100, 10000]; // 10m, 10km
	timeInterval = timeInterval || [2 * 60 * 1000, 60 * 60 * 1000]; // 2 minutes, 1 hour
    byStatus = byStatus || false; // split by status as well?

	var segments = [];

	var currentSegment = [];
	var lastPosition = null;
	var n = data.length;
	for (var i = 0; i < n; i++) {

		// current position
		var currentPosition = data[i];

		// distance?
		if (lastPosition != null) {
			var distance = calculateDistanceHaversine(lastPosition.location, currentPosition.location);
			var interval = Math.abs(Date.parse(lastPosition.timestamp) - Date.parse(currentPosition.timestamp));
			var newStatus = false;
			if (byStatus && lastPosition.status != currentPosition.status) {
			    newStatus = true;
            }
			if (newStatus ||
                distance > separationRadius[1] ||
                interval > timeInterval[1] ||
                (interval > timeInterval[0] && distance > separationRadius[0])) {
                // terminate current segment
                segments.push(currentSegment);
                currentSegment = [];
            }
		}

		// add position to segment
		currentSegment.push(currentPosition);

		// update lastPosition
		lastPosition = currentPosition;

	}

	// anything left?
	if (currentSegment.length > 0) {
        // terminate last segment
        segments.push(currentSegment);
    }

	return segments;

}

function createMarker(call_or_update, icon) {

    // default marker
    icon = icon || L.icon({
        iconUrl: '/static/icons/flag/blue.svg',
        iconSize: [15, 30],
    });

    var location = call_or_update.location;
    return L.marker(
        [location.latitude, location.longitude],
        {icon: icon});

}

function createSegmentLine(map, updates) {

	// Store data in an array
	var latlngs = [];
	updates.forEach(function(update) {

		// push location
		var loc = update.location;
		latlngs.push([loc.latitude, loc.longitude]);

    });

	// Add line to map
	console.log('Adding segment');
	return L.polyline(latlngs, {color: "red"});

}

// Interact with widget to add an ambulance route
function addAmbulanceRoute(map, data, byStatus) {

    byStatus = byStatus || false;

    // paginated?
    if ('results' in data) {
        data = data.results
    }

    // short return
    if (data.length == 0)
        return;

    // break segments
    var segments = breakSegments(data, byStatus);

    // loop on segments
    segments.forEach( function(segment, index) {

        // add segment to map
        createSegment(map, segment)
            .addTo(map.map);

    });

    // create route filter
    //createRouteFilter(segments);

    console.log('Centering map');
    map.center(data[0].location);

}

function createRouteFilter(map, segments) {

    // Add the checkbox on the top right corner for filtering.
    var container = L.DomUtil.create('div', 'filter-options bg-light');

    //Generate HTML code for checkboxes for each of the statuses.
    var filterHtml = "";

    filterHtml += '
';
    segments.forEach(function (segment, index) {

        var date = new Date(Date.parse(segment[0].timestamp));
        filterHtml += '
'
            + '<input class="chk" data-status="layer_' + index + '" type="checkbox" value="" checked >'
            + date.toLocaleString()
            + ''
            + '
';

    });
    filterHtml += "
";

    // Append html code to container
    container.innerHTML = filterHtml;

    // Add the checkboxes.
    var customControl = L.Control.extend({

        options: {
            position: 'topright'
        },

        onAdd: function (map) {
            return container;
        }

    });
    map.map.addControl(new customControl());

    // Add listener to remove status layer when filter checkbox is clicked
    $('.chk').change(function () {

        // Which layer?
        var layer = map.getLayerPane(this.getAttribute('data-status'));

        if (this.checked) {
            layer.style.display = 'block';
        } else {
            layer.style.display = 'none';
        }

    });

};
<commit_msg>Improving call_detail page.<commit_after>function calculateDistanceHaversine(location1, location2, radius) {

	radius = radius || 6371e3;

	// convert latitude and longitude to radians first
    var lat1 = Math.PI * location1.latitude / 180;
    var lat2 = Math.PI * location2.latitude / 180;
    var d_phi = lat2 - lat1;
    var d_lambda = Math.PI * (location2.longitude - location1.longitude) / 180;

    var a = Math.sin(d_phi / 2) * Math.sin(d_phi / 2) + Math.cos(lat1) * Math.cos(lat2) * Math.sin(d_lambda / 2) * Math.sin(d_lambda / 2);
    var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));

    // console.log('|| {} - {} ||= {}'.format(location2, location1, earth_radius * c))

    return radius * c;
}

function breakSegments(data, byStatus, separationRadius, timeInterval) {

	separationRadius = separationRadius || [100, 10000]; // 10m, 10km
	timeInterval = timeInterval || [2 * 60 * 1000, 60 * 60 * 1000]; // 2 minutes, 1 hour
    byStatus = byStatus || false; // split by status as well?

	var segments = [];

	var currentSegment = [];
	var lastPosition = null;
	var n = data.length;
	for (var i = 0; i < n; i++) {

		// current position
		var currentPosition = data[i];

		// distance?
		if (lastPosition != null) {
			var distance = calculateDistanceHaversine(lastPosition.location, currentPosition.location);
			var interval = Math.abs(Date.parse(lastPosition.timestamp) - Date.parse(currentPosition.timestamp));
			var newStatus = false;
			if (byStatus && lastPosition.status != currentPosition.status) {
			    newStatus = true;
            }
			if (newStatus ||
                distance > separationRadius[1] ||
                interval > timeInterval[1] ||
                (interval > timeInterval[0] && distance > separationRadius[0])) {
                // terminate current segment
                segments.push(currentSegment);
                currentSegment = [];
            }
		}

		// add position to segment
		currentSegment.push(currentPosition);

		// update lastPosition
		lastPosition = currentPosition;

	}

	// anything left?
	if (currentSegment.length > 0) {
        // terminate last segment
        segments.push(currentSegment);
    }

	return segments;

}

function createMarker(call_or_update, icon) {

    // default marker
    icon = icon || L.icon({
        iconUrl: '/static/icons/flag/blue.svg',
        iconSize: [15, 30],
    });

    var location = call_or_update.location;
    return L.marker(
        [location.latitude, location.longitude],
        {icon: icon});

}

function createSegmentLine(map, updates) {

	// Store data in an array
	var latlngs = [];
	updates.forEach(function(update) {

		// push location
		var loc = update.location;
		latlngs.push([loc.latitude, loc.longitude]);

    });

	// Add line to map
	console.log('Adding segment');
	return L.polyline(latlngs, {color: "red"});

}

// Interact with widget to add an ambulance route
function addAmbulanceRoute(map, data, byStatus) {

    byStatus = byStatus || false;

    // paginated?
    if ('results' in data) {
        data = data.results
    }

    // short return
    if (data.length == 0)
        return;

    // break segments
    var segments = breakSegments(data, byStatus);

    // loop on segments
    segments.forEach( function(segment, index) {

        // add segment to map
        createSegmentLine(map, segment)
            .addTo(map.map);

    });

    // create route filter
    //createRouteFilter(segments);

    console.log('Centering map');
    map.center(data[0].location);

}

function createRouteFilter(map, segments) {

    // Add the checkbox on the top right corner for filtering.
    var container = L.DomUtil.create('div', 'filter-options bg-light');

    //Generate HTML code for checkboxes for each of the statuses.
    var filterHtml = "";

    filterHtml += '
';
    segments.forEach(function (segment, index) {

        var date = new Date(Date.parse(segment[0].timestamp));
        filterHtml += '
'
            + '<input class="chk" data-status="layer_' + index + '" type="checkbox" value="" checked >'
            + date.toLocaleString()
            + ''
            + '
';

    });
    filterHtml += "
";

    // Append html code to container
    container.innerHTML = filterHtml;

    // Add the checkboxes.
    var customControl = L.Control.extend({

        options: {
            position: 'topright'
        },

        onAdd: function (map) {
            return container;
        }

    });
    map.map.addControl(new customControl());

    // Add listener to remove status layer when filter checkbox is clicked
    $('.chk').change(function () {

        // Which layer?
        var layer = map.getLayerPane(this.getAttribute('data-status'));

        if (this.checked) {
            layer.style.display = 'block';
        } else {
            layer.style.display = 'none';
        }

    });

};
<|endoftext|>


<commit_before>var muting_ui = (function () {

var exports = {};

function timestamp_ms() {
    return (new Date()).getTime();
}

var last_topic_update = 0;

exports.rerender = function () {
    // Note: We tend to optimistically rerender muting preferences before
    // the back end actually acknowledges the mute.  This gives a more
    // immediate feel to the user, and if the back end fails temporarily,
    // re-doing a mute or unmute is a pretty recoverable thing.

    stream_list.update_streams_sidebar();
    if (current_msg_list.muting_enabled) {
        current_msg_list.update_muting_and_rerender();
    }
    if (current_msg_list !== home_msg_list) {
        home_msg_list.update_muting_and_rerender();
    }
};

exports.notify_with_undo_option = (function () {
    var meta = {
        hide_me_time: null,
        alert_hover_state: false,
        $container: null,
    };
    var animate = {
        fadeOut: function () {
            if (meta.$container) {
                meta.$container.fadeOut(500).removeClass("show");
            }
        },
        fadeIn: function () {
            if (meta.$container) {
                meta.$container.fadeIn(500).addClass("show");
            }
        },
    };
    setInterval(function () {
        if (meta.hide_me_time < new Date().getTime() && !meta.alert_hover_state) {
            animate.fadeOut();
        }
    }, 100);

    return function (stream_id, topic) {
        var stream_name = stream_data.maybe_get_stream_name(stream_id);

        var $exit = $("#unmute_muted_topic_notification .exit-me");

        meta.undo = function () {
            // it should reference the meta variable and not get stuck with
            // a pass-by-value of stream, topic.
            exports.unmute(stream_id, topic);
        };

        if (!meta.$container) {
            meta.$container = $("#unmute_muted_topic_notification");

            $exit.click(function () {
                animate.fadeOut();
            });

            meta.$container.find("#unmute").click(function () {
                // it should reference the meta variable and not get stuck with
                // a pass-by-value of stream, topic.
                meta.undo();
                animate.fadeOut();
            });
        }

        // add a four second delay before closing up.
        meta.hide_me_time = new Date().getTime() + 4000;

        meta.$container.find(".stream").text(stream_name);
        meta.$container.find(".topic").text(topic);

        animate.fadeIn();

        // if the user mouses over the notification, don't hide it.
        meta.$container.mouseenter(function () {
            meta.alert_hover_state = true;
        });

        // once the user's mouse leaves the notification, restart the countdown.
        meta.$container.mouseleave(function () {
            meta.alert_hover_state = false;
            // add at least 2000ms but if more than that exists just keep the
            // current amount.
            meta.hide_me_time = Math.max(meta.hide_me_time, new Date().getTime() + 2000);
        });
    };
}());

exports.dismiss_mute_confirmation = function () {
    var $container = $("#unmute_muted_topic_notification");
    if ($container) {
        $container.fadeOut(500).removeClass("show");
    }
};

exports.persist_mute = function (stream_id, topic_name) {
    var data = {
        stream_id: stream_id,
        topic: topic_name,
        op: 'add',
    };
    last_topic_update = timestamp_ms();
    channel.patch({
        url: '/json/users/me/subscriptions/muted_topics',
        idempotent: true,
        data: data,
    });
};

exports.persist_unmute = function (stream_id, topic_name) {
    var data = {
        stream_id: stream_id,
        topic: topic_name,
        op: 'remove',
    };
    last_topic_update = timestamp_ms();
    channel.patch({
        url: '/json/users/me/subscriptions/muted_topics',
        idempotent: true,
        data: data,
    });
};

exports.handle_updates = function (muted_topics) {
    if (timestamp_ms() < last_topic_update + 1000) {
        // This topic update is either the one that we just rendered, or,
        // much less likely, it's coming from another device and would probably
        // be overwriting this device's preferences with stale data.
        return;
    }

    exports.update_muted_topics(muted_topics);
    exports.rerender();
};

exports.update_muted_topics = function (muted_topics) {
    muting.set_muted_topics(muted_topics);
    unread_ui.update_unread_counts();
};

exports.set_up_muted_topics_ui = function (muted_topics) {
    var muted_topics_table = $("#muted_topics_table tbody");
    muted_topics_table.empty();
    _.each(muted_topics, function (tup) {
        var stream_id = tup[0];
        var topic = tup[1];

        var stream = stream_data.maybe_get_stream_name(stream_id);

        if (!stream) {
            blueslip.warn('Unknown stream_id in set_up_muted_topics_ui: ' + stream_id);
            return;
        }

        var template_data = {
            stream: stream,
            stream_id: stream_id,
            topic: topic,
        };

        var row = templates.render('muted_topic_ui_row', template_data);
        muted_topics_table.append(row);
    });
};

exports.mute = function (stream_id, topic) {
    stream_popover.hide_topic_popover();
    muting.add_muted_topic(stream_id, topic);
    unread_ui.update_unread_counts();
    exports.rerender();
    exports.persist_mute(stream_id, topic);
    exports.notify_with_undo_option(stream_id, topic);
    exports.set_up_muted_topics_ui(muting.get_muted_topics());
};

exports.unmute = function (stream_id, topic) {
    // we don't run a unmute_notify function because it isn't an issue as much
    // if someone accidentally unmutes a stream rather than if they mute it
    // and miss out on info.
    stream_popover.hide_topic_popover();
    muting.remove_muted_topic(stream_id, topic);
    unread_ui.update_unread_counts();
    exports.rerender();
    exports.persist_unmute(stream_id, topic);
    exports.set_up_muted_topics_ui(muting.get_muted_topics());
    exports.dismiss_mute_confirmation();
};

exports.toggle_mute = function (message) {
    var stream_id = message.stream_id;
    var topic = util.get_message_topic(message);

    if (muting.is_topic_muted(stream_id, topic)) {
        exports.unmute(stream_id, topic);
    } else if (message.type === 'stream') {
        exports.mute(stream_id, topic);
    }
};

return exports;
}());

if (typeof module !== 'undefined') {
    module.exports = muting_ui;
}
window.muting_ui = muting_ui;
<commit_msg>muting: Extract notify_widget class.<commit_after>var muting_ui = (function () {

var exports = {};

function timestamp_ms() {
    return (new Date()).getTime();
}

var last_topic_update = 0;

exports.rerender = function () {
    // Note: We tend to optimistically rerender muting preferences before
    // the back end actually acknowledges the mute.  This gives a more
    // immediate feel to the user, and if the back end fails temporarily,
    // re-doing a mute or unmute is a pretty recoverable thing.

    stream_list.update_streams_sidebar();
    if (current_msg_list.muting_enabled) {
        current_msg_list.update_muting_and_rerender();
    }
    if (current_msg_list !== home_msg_list) {
        home_msg_list.update_muting_and_rerender();
    }
};

exports.build_feedback_widget = function () {
    var self = {};

    var meta = {
        hide_me_time: null,
        alert_hover_state: false,
        $container: null,
    };
    var animate = {
        fadeOut: function () {
            if (meta.$container) {
                meta.$container.fadeOut(500).removeClass("show");
            }
        },
        fadeIn: function () {
            if (meta.$container) {
                meta.$container.fadeIn(500).addClass("show");
            }
        },
    };
    setInterval(function () {
        if (meta.hide_me_time < new Date().getTime() && !meta.alert_hover_state) {
            animate.fadeOut();
        }
    }, 100);

    self.show = function (opts) {
        if (!opts.populate) {
            blueslip.error('programmer needs to supply populate callback.');
            return;
        }

        meta.undo = opts.on_undo;

        if (!meta.$container) {
            meta.$container = $("#unmute_muted_topic_notification");

            meta.$container.find('.exit-me').click(function () {
                animate.fadeOut();
            });

            meta.$container.find("#unmute").click(function () {
                if (meta.undo) {
                    meta.undo();
                }
                animate.fadeOut();
            });
        }

        // add a four second delay before closing up.
        meta.hide_me_time = new Date().getTime() + 4000;

        opts.populate(meta.$container);

        animate.fadeIn();

        // if the user mouses over the notification, don't hide it.
        meta.$container.mouseenter(function () {
            meta.alert_hover_state = true;
        });

        // once the user's mouse leaves the notification, restart the countdown.
        meta.$container.mouseleave(function () {
            meta.alert_hover_state = false;
            // add at least 2000ms but if more than that exists just keep the
            // current amount.
            meta.hide_me_time = Math.max(meta.hide_me_time, new Date().getTime() + 2000);
        });
    };

    self.dismiss = function () {
        var $container = $("#unmute_muted_topic_notification");
        if ($container) {
            $container.fadeOut(500).removeClass("show");
        }
    };

    return self;
};

exports.notify_widget = exports.build_feedback_widget();

exports.persist_mute = function (stream_id, topic_name) {
    var data = {
        stream_id: stream_id,
        topic: topic_name,
        op: 'add',
    };
    last_topic_update = timestamp_ms();
    channel.patch({
        url: '/json/users/me/subscriptions/muted_topics',
        idempotent: true,
        data: data,
    });
};

exports.persist_unmute = function (stream_id, topic_name) {
    var data = {
        stream_id: stream_id,
        topic: topic_name,
        op: 'remove',
    };
    last_topic_update = timestamp_ms();
    channel.patch({
        url: '/json/users/me/subscriptions/muted_topics',
        idempotent: true,
        data: data,
    });
};

exports.handle_updates = function (muted_topics) {
    if (timestamp_ms() < last_topic_update + 1000) {
        // This topic update is either the one that we just rendered, or,
        // much less likely, it's coming from another device and would probably
        // be overwriting this device's preferences with stale data.
        return;
    }

    exports.update_muted_topics(muted_topics);
    exports.rerender();
};

exports.update_muted_topics = function (muted_topics) {
    muting.set_muted_topics(muted_topics);
    unread_ui.update_unread_counts();
};

exports.set_up_muted_topics_ui = function (muted_topics) {
    var muted_topics_table = $("#muted_topics_table tbody");
    muted_topics_table.empty();
    _.each(muted_topics, function (tup) {
        var stream_id = tup[0];
        var topic = tup[1];

        var stream = stream_data.maybe_get_stream_name(stream_id);

        if (!stream) {
            blueslip.warn('Unknown stream_id in set_up_muted_topics_ui: ' + stream_id);
            return;
        }

        var template_data = {
            stream: stream,
            stream_id: stream_id,
            topic: topic,
        };

        var row = templates.render('muted_topic_ui_row', template_data);
        muted_topics_table.append(row);
    });
};

exports.mute = function (stream_id, topic) {
    var stream_name = stream_data.maybe_get_stream_name(stream_id);

    stream_popover.hide_topic_popover();
    muting.add_muted_topic(stream_id, topic);
    unread_ui.update_unread_counts();
    exports.rerender();
    exports.persist_mute(stream_id, topic);
    exports.notify_widget.show({
        populate: function (container) {
            container.find(".stream").text(stream_name);
            container.find(".topic").text(topic);
        },
        on_undo: function () {
            exports.unmute(stream_id, topic);
        },
    });
    exports.set_up_muted_topics_ui(muting.get_muted_topics());
};

exports.unmute = function (stream_id, topic) {
    // we don't run a unmute_notify function because it isn't an issue as much
    // if someone accidentally unmutes a stream rather than if they mute it
    // and miss out on info.
    stream_popover.hide_topic_popover();
    muting.remove_muted_topic(stream_id, topic);
    unread_ui.update_unread_counts();
    exports.rerender();
    exports.persist_unmute(stream_id, topic);
    exports.set_up_muted_topics_ui(muting.get_muted_topics());
    exports.notify_widget.dismiss();
};

exports.toggle_mute = function (message) {
    var stream_id = message.stream_id;
    var topic = util.get_message_topic(message);

    if (muting.is_topic_muted(stream_id, topic)) {
        exports.unmute(stream_id, topic);
    } else if (message.type === 'stream') {
        exports.mute(stream_id, topic);
    }
};

return exports;
}());

if (typeof module !== 'undefined') {
    module.exports = muting_ui;
}
window.muting_ui = muting_ui;
<|endoftext|>


<commit_before>// Temp: this will be delivered via the template preprocessor.
AVAILABLE_LAYOUTS = {
    'face': '2163152',
    'status': '2163153',
    'related': '2163154',
    'dashboard': '2163156'
};

IONUX.Router = Backbone.Router.extend({
    routes: {
        "": "dashboard",
        ":resource_type/list/": "collection",
        ":resource_type/command/:resource_id/": "command",
        ":resource_type/:view_type/:resource_id/" : "page",
        // "instruments/:instrument_id/command/": "instrument_command_facepage",
        // "userprofile/": "user_profile",
    },

    dashboard: function(){
        this._reset();
    },

    // Collection 'face pages'
    collection: function(resource_type){
        $('#error').hide();
        $('#dynamic-container').show();
        $('#dynamic-container').html($('#2163152').html());
        $('.span9 li,.span3 li').hide();

        var resources = new IONUX.Collections.Resources(null, {resource_type: resource_type});
        resources.fetch().success(function(data){
            $('li.Collection ,div.Collection').show(); // Show elements based on current view/CSS class, i.e. .InstrumentDevice
            $('.span9 ul').find('li.Collection:first').find('a').click(); // Manually Set the first tabs 'active'

            // Todo: better way of finding the container for the collection.
            var elmt_id = $('.Collection .table_ooi:first').parent('div').attr('id');
            var resource_collection = new IONUX.Collections.Resources(data.data, {resource_type: resource_type});
            new IONUX.Views.Collection({el: '#' + elmt_id, collection: resource_collection, resource_type: resource_type}).render().el;
        });
    },

    // Face, status, related pages
    page: function(resource_type, view_type, resource_id){
        $('#error').hide();
        $('#dynamic-container').show();
        $('#dynamic-container').html($('#' + AVAILABLE_LAYOUTS[view_type]).html());
        $('.span9 li,.span3 li').hide();

        var resource_extension = new IONUX.Models.ResourceExtension({resource_type: resource_type, resource_id: resource_id});
        resource_extension.fetch()
            .success(function(model, resp) {
                render_page(resource_type, model);
            })
            .error(function(model, resp) {
                render_error();
            });
    },

    command: function(resource_type, resource_id){
        var resource_extension = new IONUX.Models.ResourceExtension({resource_type: 'InstrumentDevice', resource_id: resource_id});
        var instrument_command = new IONUX.Views.InstrumentCommandFacepage({model: resource_extension});
        resource_extension.fetch();
        window.MODEL_DATA = resource_extension;
    },

    // KEPT FOR REFERENCE
    // user_profile: function() {
    //     this._reset();
    //     var fpModel = new IONUX.Models.UserRegistrationModel();
    //     new IONUX.Views.UserRegistration({model:fpModel});
    //     fpModel.fetch();
    // },

    // KEPT FOR REFERENCE - ALEX'S USER REQUESTS
    // observatory_facepage: function(observatory_id){
    //     this._reset();
    //     var fpModel = new IONUX.Models.ObservatoryFacepageModel({observatory_id:observatory_id});
    //     new IONUX.Views.ObservatoryFacepage({model:fpModel});
    //     fpModel.fetch();
    //
    //     var urCollection = new IONUX.Collections.UserRequestCollection();
    //     urCollection.observatory_id = observatory_id; //XXX better way to set this?
    //     var userRequestsView = new IONUX.Views.UserRequestsView({collection:urCollection, facepage_model: fpModel});
    //     urCollection.fetch();
    // },

    handle_navigation: function(){
        var self = this;
        $(document).on("click", "a", function(e) {
            if ($(e.target).hasClass('external')) return true;

            // Catch Bootstrap's tabs hash so URL doesn't change, example: "InstrumentDevice/list/" to "/2150593"
            if ($(e.target).attr('data-toggle') == 'tab') return true;
            self.navigate($(this).attr('href'), {trigger:true});
            return false;
        });
    },

    // graceful Backbone handling of full page refresh on non '/' url.
    handle_url: function(current_url){
        if (current_url != "/"){
            this.navigate(current_url, {trigger:true});
        }
    },

    _reset: function(){ //reset the UI
        $(".viewcontainer").hide();
    }

});

// Prepare response from server for IONUX.Views.DataTable;
// function prepare_table_data(data, columns) {
//     var table = {headers: [], data: []}
//
//     // // Headers
//     // if (!columns) var columns = _.keys(data[0]);
//     // _.each(columns, function(column){
//     //     table.headers.push({'sTitle': column});
//     // });
//
//     // Data
//     _.each(data, function(row) {
//         var row_values = _.pick(row, columns);
//         var row_array = _.toArray(row_values);
//         table.data.push(row_array);
//     });
//     return table
// };

// Get values from string notation, example:
//
 will
// result in ['resource']['serial_number']
function get_descendant_properties(obj, desc) {
    var arr = desc.split(".");
    while(arr.length && (obj = obj[arr.shift()]));
    return obj;
};

function render_page(resource_type, model) {

    // Catch specific routes that need generic resources...
    if (resource_type == 'InstrumentModel' || resource_type == ('PlatformModel')) {
        var resource_type = 'DeviceModel';
    } else if (resource_type == 'Observatory') {
        var resource_type = 'Org'
    };

    window.MODEL_DATA = model.data;

    var attribute_group_elmts = $('.'+resource_type+' .attribute_group_ooi');
    _.each(attribute_group_elmts, function(el){
        var data_path = $(el).data('path');
        var data = get_descendant_properties(window.MODEL_DATA, data_path);

        new IONUX.Views.AttributeGroup({el: $(el), data: window.MODEL_DATA}).render().el;
        append_info_level(el);
    });

    var text_static_elmts = $('.'+resource_type+' .text_static_ooi');
    _.each(text_static_elmts, function(el){
        new IONUX.Views.TextStatic({el: $(el)}).render().el;
        append_info_level(el);
    });

    var text_short_elmts = $('.'+resource_type+' .text_short_ooi');
    _.each(text_short_elmts, function(el){
        new IONUX.Views.TextShort({el: $(el), data_model: window.MODEL_DATA}).render().el;
        append_info_level(el);
    });

    var text_extended_elmts = $('.'+resource_type+' .text_extended_ooi');
    _.each(text_extended_elmts, function(el){
        new IONUX.Views.TextExtended({el: $(el), data_model: window.MODEL_DATA}).render().el;
        append_info_level(el);
    });

    var icon_elmts = $('.'+resource_type+' .icon_ooi');
    _.each(icon_elmts, function(el) {
        new IONUX.Views.Icon({el: $(el)}).render().el;
        append_info_level(el);
    });

    var image_elmts = $('.'+resource_type+' .image_ooi');
    _.each(image_elmts, function(el) {
        new IONUX.Views.Image({el: $(el)}).render().el;
        append_info_level(el);
    });

    var badge_elmts = $('.'+resource_type+' .badge_ooi');
    _.each(badge_elmts, function(el) {
        new IONUX.Views.Badge({el: $(el), data_model: window.MODEL_DATA}).render().el;
        append_info_level(el);
    });

    var list_elmts = $('.'+resource_type+' .list_ooi');
    _.each(list_elmts, function(el) {
        new IONUX.Views.List({el: $(el), data_model: window.MODEL_DATA}).render().el;
    });

    var table_elmts = $('.'+resource_type+' .table_ooi');
    _.each(table_elmts, function(el) {
        var data_path = $(el).data('path');
        if (data_path) {
            // var raw_table_data = window.MODEL_DATA[data_path];
            var raw_table_data = get_descendant_properties(window.MODEL_DATA, data_path);
            new IONUX.Views.DataTable({el: $(el), data: raw_table_data});
        } else {
            new IONUX.Views.DataTable({el: $(el), data: []});
        };
    });

    var extent_geospatial_elmts = $('.'+resource_type+' .extent_geospatial_ooi');
    _.each(extent_geospatial_elmts, function(el) {
        new IONUX.Views.ExtentGeospatial({el: $(el)}).render().el;
    });

    var extent_vertical_elmts = $('.'+resource_type+' .extent_vertical_ooi');
    _.each(extent_vertical_elmts, function(el) {
        new IONUX.Views.ExtentVertical({el: $(el)}).render().el;
    });

    var extent_temporal_elmts = $('.'+resource_type+' .extent_temporal_ooi');
    _.each(extent_temporal_elmts, function(el) {
        new IONUX.Views.ExtentTemporal({el: $(el)}).render().el;
    });

    var checkbox_elmts = $('.'+resource_type+' .checkbox_ooi');
    _.each(checkbox_elmts, function(el) {
        new IONUX.Views.Checkbox({el: $(el), data_model: window.MODEL_DATA}).render().el;
    });

    // Show the relevant elements and click to enable the Bootstrap tabs.
    $('li.' + resource_type + ', div.' + resource_type).show();
    $('.span9 ul, .span3 ul').find('li.' + resource_type + ':first').find('a').click();

    $('.tab-pane').find('.'+resource_type+':visible:first').css('margin-left', 0)
};

function render_error(){
    $('#dynamic-container').hide();
    $('#error').show();
};

function append_info_level(el) {
    // var info_level = $(el).data('level');
    // if (info_level || info_level == '0') {
    //     $(el).append(''+info_level+'
');
    // };
};<commit_msg>DataTable router fixer for "unknown" paths.<commit_after>// Temp: this will be delivered via the template preprocessor.
AVAILABLE_LAYOUTS = {
    'face': '2163152',
    'status': '2163153',
    'related': '2163154',
    'dashboard': '2163156'
};

IONUX.Router = Backbone.Router.extend({
    routes: {
        "": "dashboard",
        ":resource_type/list/": "collection",
        ":resource_type/command/:resource_id/": "command",
        ":resource_type/:view_type/:resource_id/" : "page",
        // "instruments/:instrument_id/command/": "instrument_command_facepage",
        // "userprofile/": "user_profile",
    },

    dashboard: function(){
        this._reset();
    },

    // Collection 'face pages'
    collection: function(resource_type){
        $('#error').hide();
        $('#dynamic-container').show();
        $('#dynamic-container').html($('#2163152').html());
        $('.span9 li,.span3 li').hide();

        var resources = new IONUX.Collections.Resources(null, {resource_type: resource_type});
        resources.fetch().success(function(data){
            $('li.Collection ,div.Collection').show(); // Show elements based on current view/CSS class, i.e. .InstrumentDevice
            $('.span9 ul').find('li.Collection:first').find('a').click(); // Manually Set the first tabs 'active'

            // Todo: better way of finding the container for the collection.
            var elmt_id = $('.Collection .table_ooi:first').parent('div').attr('id');
            var resource_collection = new IONUX.Collections.Resources(data.data, {resource_type: resource_type});
            new IONUX.Views.Collection({el: '#' + elmt_id, collection: resource_collection, resource_type: resource_type}).render().el;
        });
    },

    // Face, status, related pages
    page: function(resource_type, view_type, resource_id){
        $('#error').hide();
        $('#dynamic-container').show();
        $('#dynamic-container').html($('#' + AVAILABLE_LAYOUTS[view_type]).html());
        $('.span9 li,.span3 li').hide();

        var resource_extension = new IONUX.Models.ResourceExtension({resource_type: resource_type, resource_id: resource_id});
        resource_extension.fetch()
            .success(function(model, resp) {
                render_page(resource_type, model);
            })
            .error(function(model, resp) {
                render_error();
            });
    },

    command: function(resource_type, resource_id){
        var resource_extension = new IONUX.Models.ResourceExtension({resource_type: 'InstrumentDevice', resource_id: resource_id});
        var instrument_command = new IONUX.Views.InstrumentCommandFacepage({model: resource_extension});
        resource_extension.fetch();
        window.MODEL_DATA = resource_extension;
    },

    // KEPT FOR REFERENCE
    // user_profile: function() {
    //     this._reset();
    //     var fpModel = new IONUX.Models.UserRegistrationModel();
    //     new IONUX.Views.UserRegistration({model:fpModel});
    //     fpModel.fetch();
    // },

    // KEPT FOR REFERENCE - ALEX'S USER REQUESTS
    // observatory_facepage: function(observatory_id){
    //     this._reset();
    //     var fpModel = new IONUX.Models.ObservatoryFacepageModel({observatory_id:observatory_id});
    //     new IONUX.Views.ObservatoryFacepage({model:fpModel});
    //     fpModel.fetch();
    //
    //     var urCollection = new IONUX.Collections.UserRequestCollection();
    //     urCollection.observatory_id = observatory_id; //XXX better way to set this?
    //     var userRequestsView = new IONUX.Views.UserRequestsView({collection:urCollection, facepage_model: fpModel});
    //     urCollection.fetch();
    // },

    handle_navigation: function(){
        var self = this;
        $(document).on("click", "a", function(e) {
            if ($(e.target).hasClass('external')) return true;

            // Catch Bootstrap's tabs hash so URL doesn't change, example: "InstrumentDevice/list/" to "/2150593"
            if ($(e.target).attr('data-toggle') == 'tab') return true;
            self.navigate($(this).attr('href'), {trigger:true});
            return false;
        });
    },

    // graceful Backbone handling of full page refresh on non '/' url.
    handle_url: function(current_url){
        if (current_url != "/"){
            this.navigate(current_url, {trigger:true});
        }
    },

    _reset: function(){ //reset the UI
        $(".viewcontainer").hide();
    }

});

// Prepare response from server for IONUX.Views.DataTable;
// function prepare_table_data(data, columns) {
//     var table = {headers: [], data: []}
//
//     // // Headers
//     // if (!columns) var columns = _.keys(data[0]);
//     // _.each(columns, function(column){
//     //     table.headers.push({'sTitle': column});
//     // });
//
//     // Data
//     _.each(data, function(row) {
//         var row_values = _.pick(row, columns);
//         var row_array = _.toArray(row_values);
//         table.data.push(row_array);
//     });
//     return table
// };

// Get values from string notation, example:
//
 will
// result in ['resource']['serial_number']
function get_descendant_properties(obj, desc) {
    var arr = desc.split(".");
    while(arr.length && (obj = obj[arr.shift()]));
    return obj;
};

function render_page(resource_type, model) {

    // Catch specific routes that need generic resources...
    if (resource_type == 'InstrumentModel' || resource_type == ('PlatformModel')) {
        var resource_type = 'DeviceModel';
    } else if (resource_type == 'Observatory') {
        var resource_type = 'Org'
    };

    window.MODEL_DATA = model.data;

    var attribute_group_elmts = $('.'+resource_type+' .attribute_group_ooi');
    _.each(attribute_group_elmts, function(el){
        var data_path = $(el).data('path');
        var data = get_descendant_properties(window.MODEL_DATA, data_path);

        new IONUX.Views.AttributeGroup({el: $(el), data: window.MODEL_DATA}).render().el;
        append_info_level(el);
    });

    var text_static_elmts = $('.'+resource_type+' .text_static_ooi');
    _.each(text_static_elmts, function(el){
        new IONUX.Views.TextStatic({el: $(el)}).render().el;
        append_info_level(el);
    });

    var text_short_elmts = $('.'+resource_type+' .text_short_ooi');
    _.each(text_short_elmts, function(el){
        new IONUX.Views.TextShort({el: $(el), data_model: window.MODEL_DATA}).render().el;
        append_info_level(el);
    });

    var text_extended_elmts = $('.'+resource_type+' .text_extended_ooi');
    _.each(text_extended_elmts, function(el){
        new IONUX.Views.TextExtended({el: $(el), data_model: window.MODEL_DATA}).render().el;
        append_info_level(el);
    });

    var icon_elmts = $('.'+resource_type+' .icon_ooi');
    _.each(icon_elmts, function(el) {
        new IONUX.Views.Icon({el: $(el)}).render().el;
        append_info_level(el);
    });

    var image_elmts = $('.'+resource_type+' .image_ooi');
    _.each(image_elmts, function(el) {
        new IONUX.Views.Image({el: $(el)}).render().el;
        append_info_level(el);
    });

    var badge_elmts = $('.'+resource_type+' .badge_ooi');
    _.each(badge_elmts, function(el) {
        new IONUX.Views.Badge({el: $(el), data_model: window.MODEL_DATA}).render().el;
        append_info_level(el);
    });

    var list_elmts = $('.'+resource_type+' .list_ooi');
    _.each(list_elmts, function(el) {
        new IONUX.Views.List({el: $(el), data_model: window.MODEL_DATA}).render().el;
    });

    var table_elmts = $('.'+resource_type+' .table_ooi');
    _.each(table_elmts, function(el) {
        var data_path = $(el).data('path');

        if (!data_path.substring(0,6) == 'unknown') {
            var raw_table_data = window.MODEL_DATA[data_path];
            var raw_table_data = get_descendant_properties(window.MODEL_DATA, data_path);
            new IONUX.Views.DataTable({el: $(el), data: raw_table_data});
        } else {
            // new IONUX.Views.DataTable({el: $(el), data: []});
        };
    });

    var extent_geospatial_elmts = $('.'+resource_type+' .extent_geospatial_ooi');
    _.each(extent_geospatial_elmts, function(el) {
        new IONUX.Views.ExtentGeospatial({el: $(el)}).render().el;
    });

    var extent_vertical_elmts = $('.'+resource_type+' .extent_vertical_ooi');
    _.each(extent_vertical_elmts, function(el) {
        new IONUX.Views.ExtentVertical({el: $(el)}).render().el;
    });

    var extent_temporal_elmts = $('.'+resource_type+' .extent_temporal_ooi');
    _.each(extent_temporal_elmts, function(el) {
        new IONUX.Views.ExtentTemporal({el: $(el)}).render().el;
    });

    var checkbox_elmts = $('.'+resource_type+' .checkbox_ooi');
    _.each(checkbox_elmts, function(el) {
        new IONUX.Views.Checkbox({el: $(el), data_model: window.MODEL_DATA}).render().el;
    });

    // Show the relevant elements and click to enable the Bootstrap tabs.
    $('li.' + resource_type + ', div.' + resource_type).show();
    $('.span9 ul, .span3 ul').find('li.' + resource_type + ':first').find('a').click();

    $('.tab-pane').find('.'+resource_type+':visible:first').css('margin-left', 0)
};

function render_error(){
    $('#dynamic-container').hide();
    $('#error').show();
};

function append_info_level(el) {
    // var info_level = $(el).data('level');
    // if (info_level || info_level == '0') {
    //     $(el).append(''+info_level+'
');
    // };
};<|endoftext|>


<commit_before>// Temp: this will be delivered via the template preprocessor.
AVAILABLE_LAYOUTS = {
    'face': '2163152',
    'status': '2163153',
    'related': '2163154',
    'dashboard': '2163156',
    'command': '2163157'
};

IONUX.Router = Backbone.Router.extend({
    routes: {
        "": "dashboard",
        ":resource_type/list/": "collection",
        ":resource_type/command/:resource_id/": "command",
        ":resource_type/:view_type/:resource_id/" : "page",
        // "instruments/:instrument_id/command/": "instrument_command_facepage",
        // "userprofile/": "user_profile",
    },

    dashboard: function(){
        this._reset();
        $('#dynamic-container').html($('#' + AVAILABLE_LAYOUTS['dashboard']).html()).show();
        $('.Collection').show();

        $('.Collection .map_ooi').html('
View Larger Map')
        new IONUX.Views.Footer({resource_id: null, resource_type: null}).render().el;
    },

    // Collection 'face pages'
    collection: function(resource_type){
        $('#error').hide();
        $('#dynamic-container').show();
        $('#dynamic-container').html($('#2163152').html());
        $('.span9 li,.span3 li').hide();

        var resources = new IONUX.Collections.Resources(null, {resource_type: resource_type});
        resources.fetch().success(function(data){
            $('li.Collection ,div.Collection').show(); // Show elements based on current view/CSS class, i.e. .InstrumentDevice
            $('.span9').find('li.Collection:first').find('a').click(); // Manually Set the first tabs 'active'

            // Todo: better way of finding the container for the collection.
            var elmt_id = $('.v02 .Collection .table_ooi').first();
            var resource_collection = new IONUX.Collections.Resources(data.data, {resource_type: resource_type});
            new IONUX.Views.Collection({el: elmt_id, collection: resource_collection, resource_type: resource_type}).render().el;
        });

        // Insert footer and buttons
        new IONUX.Views.Footer({resource_id: null, resource_type: resource_type}).render().el;

    },

    // Face, status, related pages
    page: function(resource_type, view_type, resource_id){
        $('#error').hide();
        $('#dynamic-container').show();
        $('#dynamic-container').html($('#' + AVAILABLE_LAYOUTS[view_type]).html());
        $('.span9 li,.span3 li').hide();

        var resource_extension = new IONUX.Models.ResourceExtension({resource_type: resource_type, resource_id: resource_id});
        resource_extension.fetch()
            .success(function(model, resp) {
                render_page(resource_type, resource_id, model);
            })
            .error(function(model, resp) {
                render_error();
            });

        // Insert footer and buttons
        new IONUX.Views.Footer({resource_id: resource_id, resource_type: resource_type}).render().el;

    },

    command: function(resource_type, resource_id){
        $('#error').hide();
        $('#dynamic-container').show();
        $('#dynamic-container').html($('#' + AVAILABLE_LAYOUTS['command']).html());
        $('.span9 li,.span3 li').hide();

        // Hack to remove all unused dynamic elements, to be replaced
        // with Backbone views below.
        $('.v02').empty()

        // Determine Instrument or Platform command page is called.
        var resource_extension = new IONUX.Models.ResourceExtension({resource_type: resource_type, resource_id: resource_id});
        if (resource_type == 'InstrumentDevice') {
            new IONUX.Views.InstrumentCommandFacepage({model: resource_extension, el: '.v02'});
        } else if (resource_type == 'PlatformDevice') {
            new IONUX.Views.PlatformCommandFacepage({model: resource_extension, el: '.v02'});
        };

        resource_extension.fetch()
            .success(function(model, resp) {
                render_page(resource_type, resource_id, model);
                // $('li.' + resource_type + ', div.' + resource_type).show();
                // $('.span9 ul, .span3 ul').find('li.' + resource_type + ':first').find('a').click();
                // $('.tab-pane').find('.'+resource_type+':visible:first').css('margin-left', 0);
                // window.MODEL_DATA = model.data;
            });
    },

    // KEPT FOR REFERENCE
    // user_profile: function() {
    //     this._reset();
    //     var fpModel = new IONUX.Models.UserRegistrationModel();
    //     new IONUX.Views.UserRegistration({model:fpModel});
    //     fpModel.fetch();
    // },

    // KEPT FOR REFERENCE - ALEX'S USER REQUESTS
    // observatory_facepage: function(observatory_id){
    //     this._reset();
    //     var fpModel = new IONUX.Models.ObservatoryFacepageModel({observatory_id:observatory_id});
    //     new IONUX.Views.ObservatoryFacepage({model:fpModel});
    //     fpModel.fetch();
    //
    //     var urCollection = new IONUX.Collections.UserRequestCollection();
    //     urCollection.observatory_id = observatory_id; //XXX better way to set this?
    //     var userRequestsView = new IONUX.Views.UserRequestsView({collection:urCollection, facepage_model: fpModel});
    //     urCollection.fetch();
    // },

    handle_navigation: function(){
        var self = this;
        $(document).on("click", "a", function(e) {
            if ($(e.target).hasClass('external')) return true;

            // Catch Bootstrap's tabs hash so URL doesn't change, example: "InstrumentDevice/list/" to "/2150593"
            if ($(e.target).attr('data-toggle') == 'tab') return true;
            self.navigate($(this).attr('href'), {trigger:true});
            return false;
        });
    },

    // graceful Backbone handling of full page refresh on non '/' url.
    handle_url: function(current_url){
        if (current_url != "/"){
            this.navigate(current_url, {trigger:true});
        }
    },

    _reset: function(){ //reset the UI
        $(".viewcontainer").hide();
    }

});

// Prepare response from server for IONUX.Views.DataTable;
// function prepare_table_data(data, columns) {
//     var table = {headers: [], data: []}
//
//     // // Headers
//     // if (!columns) var columns = _.keys(data[0]);
//     // _.each(columns, function(column){
//     //     table.headers.push({'sTitle': column});
//     // });
//
//     // Data
//     _.each(data, function(row) {
//         var row_values = _.pick(row, columns);
//         var row_array = _.toArray(row_values);
//         table.data.push(row_array);
//     });
//     return table
// };

// Get values from string notation, example:
//
 will
// result in ['resource']['serial_number']
function get_descendant_properties(obj, desc) {
    var arr = desc.split(".");
    while(arr.length && (obj = obj[arr.shift()]));
    return obj;
};

function render_page(resource_type, resource_id, model) {
    // Catch and set derivative resources
    if (resource_type == 'InstrumentModel' || resource_type == 'PlatformModel' || resource_type == 'SensorModel') {
        var resource_type = 'DeviceModel';
    } else if (resource_type == 'Observatory' || resource_type == 'InstrumentSite' || resource_type == 'PlatformSite' || resource_type == 'Subsite') {
        var resource_type = 'Site';
    } else if (resource_type == 'SensorDevice') {
        var resource_type = 'Device';
    } else if (resource_type == 'DataProcess') {
        var resource_type = 'TaskableResource';
    } else if (resource_type == 'UserRole') {
        var resource_type = 'InformationResource';
    };

    window.MODEL_DATA = model.data;
    window.MODEL_DATA['resource_type'] = resource_type;

    var attribute_group_elmts = $('.'+resource_type+' .attribute_group_ooi');
    _.each(attribute_group_elmts, function(el){
        var data_path = $(el).data('path');
        var data = get_descendant_properties(window.MODEL_DATA, data_path);

        new IONUX.Views.AttributeGroup({el: $(el), data: window.MODEL_DATA}).render().el;
        append_info_level(el);
    });

    var text_static_elmts = $('.'+resource_type+' .text_static_ooi');
    _.each(text_static_elmts, function(el){
        new IONUX.Views.TextStatic({el: $(el)}).render().el;
        append_info_level(el);
    });

    var text_short_elmts = $('.'+resource_type+' .text_short_ooi');
    _.each(text_short_elmts, function(el){
        new IONUX.Views.TextShort({el: $(el), data_model: window.MODEL_DATA}).render().el;
        append_info_level(el);
    });

    var text_extended_elmts = $('.'+resource_type+' .text_extended_ooi');
    _.each(text_extended_elmts, function(el){
        new IONUX.Views.TextExtended({el: $(el), data_model: window.MODEL_DATA}).render().el;
        append_info_level(el);
    });

    var icon_elmts = $('.'+resource_type+' .icon_ooi');
    _.each(icon_elmts, function(el) {
        new IONUX.Views.Icon({el: $(el)}).render().el;
        append_info_level(el);
    });

    _.each($('.'+resource_type+' .image_ooi'), function(el) {
        console.log('image');
        $(el).html($('').addClass('status_ok_mini').html(' '));
    });

    var badge_elmts = $('.'+resource_type+' .badge_ooi');
    _.each(badge_elmts, function(el) {
        new IONUX.Views.Badge({el: $(el), data_model: window.MODEL_DATA}).render().el;
        append_info_level(el);
    });

    var list_elmts = $('.'+resource_type+' .list_ooi');
    _.each(list_elmts, function(el) {
        new IONUX.Views.List({el: $(el), data_model: window.MODEL_DATA}).render().el;
    });

    var table_elmts = $('.'+resource_type+' .table_ooi');
    _.each(table_elmts, function(el) {
        var data_path = $(el).data('path');
        if (data_path.substring(0,6) !== 'unknown') var raw_table_data = get_descendant_properties(window.MODEL_DATA, data_path);
        if (raw_table_data) new IONUX.Views.DataTable({el: $(el), data: raw_table_data});
    });

    var extent_geospatial_elmts = $('.'+resource_type+' .extent_geospatial_ooi');
    _.each(extent_geospatial_elmts, function(el) {
        var data_path = $(el).data('path');
        var data = get_descendant_properties(window.MODEL_DATA, data_path);
        new IONUX.Views.ExtentGeospatial({el: $(el), data: data}).render().el;
    });

    var extent_vertical_elmts = $('.'+resource_type+' .extent_vertical_ooi');
    _.each(extent_vertical_elmts, function(el){
        var data_path = $(el).data('path');
        var data = get_descendant_properties(window.MODEL_DATA, data_path);
        console.log(data);
        if (data) new IONUX.Views.ExtentVertical({el: $(el), data: data}).render().el;
    });

    var extent_temporal_elmts = $('.'+resource_type+' .extent_temporal_ooi');
    _.each(extent_temporal_elmts, function(el) {
        new IONUX.Views.ExtentTemporal({el: $(el)}).render().el;
    });

    var checkbox_elmts = $('.'+resource_type+' .checkbox_ooi');
    _.each(checkbox_elmts, function(el) {
        new IONUX.Views.Checkbox({el: $(el), data_model: window.MODEL_DATA}).render().el;
    });

    if (resource_type == 'DataProduct') {
        var chart_elmt = $('.'+resource_type+' .chart_ooi').first();
        $('body').append($('