<commit_before>/*
This file is part of the Juju GUI, which lets users view and manage Juju
environments within a graphical interface (https://launchpad.net/juju-gui).
Copyright (C) 2017 Canonical Ltd.

This program is free software: you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3, as published by
the Free Software Foundation.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero
General Public License for more details.

You should have received a copy of the GNU Affero General Public License along
with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

'use strict';

var juju = {components: {}}; // eslint-disable-line no-unused-vars

chai.config.includeStack = true;
chai.config.truncateThreshold = 0;

describe('HeaderHelp', function() {
  let appState;

  beforeAll(function(done) {
    // By loading this file it adds the component to the juju components.
    YUI().use('header-help', function() { done(); });
  });

  beforeEach(function() {
    appState = {
      current: {},
      changeState: sinon.stub()
    };
  });

  it('renders', function () {
    const renderer = jsTestUtils.shallowRender(
      <juju.components.HeaderHelp.prototype.wrappedComponent
        appState={appState}
        gisf={false}
        user={null} />, true);
    const output = renderer.getRenderOutput();
    const instance = renderer.getMountedInstance();

    const expected =

          <juju.components.SvgIcon name="help_16"
            className="header-menu__icon"
            size="16" />

              Help

        {''}

;

    assert.deepEqual(output, expected);
  });

  describe('menu', function () {
    const issueUrl = 'https://github.com/juju/juju-gui/issues';
    const loggedInIssueUrl = 'https://jujucharms.com/support';
    const docsUrl = 'https://jujucharms.com/docs/stable/getting-started';

    it('opens a menu when clicked', function () {
      const renderer = jsTestUtils.shallowRender(
        <juju.components.HeaderHelp.prototype.wrappedComponent
          appState={appState}
          gisf={false}
          user={null} />, true);
      const instance = renderer.getMountedInstance();
      instance.toggleHelpMenu();
      const output = renderer.getRenderOutput();

      assert.equal(output.props.children.length, 2);
      assert.deepEqual(output.props.children[0].props.className,
        'header-menu__button header-menu__show-menu');

      const expected = (<juju.components.Panel
        instanceName="header-menu__menu"
        visible={true}>

                View Documentation

              File Issue

                Keyboard shortcuts

                  Shift + ?

        </juju.components.Panel>);
      expect(output.props.children[1]).toEqualJSX(expected);
    });

    it('shows the jujuchams issues page if in gisf and logged in',
      function () {
        const renderer = jsTestUtils.shallowRender(
          <juju.components.HeaderHelp.prototype.wrappedComponent
            appState={appState}
            gisf={true}
            user={{}} />, true);
        const instance = renderer.getMountedInstance();
        instance.toggleHelpMenu();
        const output = renderer.getRenderOutput();

        assert.equal(output.props.children.length, 2);
        assert.deepEqual(output.props.children[0].props.className,
          'header-menu__button header-menu__show-menu');

        const expected = (<juju.components.Panel
          instanceName="header-menu__menu"
          visible={true}>

                  View Documentation

                Get Support

                  Keyboard shortcuts

                    Shift + ?

          </juju.components.Panel>);
        expect(output.props.children[1]).toEqualJSX(expected);
    });

    it('keyboard shortcuts link calls correct keyboard shortcut cb', () => {
      const keybindingsCallback = sinon.stub();
      const keybindings = {
        'S-/': {
          target: '#shortcut-help',
          toggle: true,
          callback: keybindingsCallback
        }
      };
      const evt = {
        stopPropagation: sinon.stub()
      }
      const div = document.createElement('div');
      div.setAttribute('id', 'shortcut-help');
      document.body.appendChild(div);
      const renderer = jsTestUtils.shallowRender(
        <juju.components.HeaderHelp.prototype.wrappedComponent
          appState={appState}
          gisf={true}
          keybindings={keybindings}
          user={{}} />, true);
      const instance = renderer.getMountedInstance();
      instance._handleShortcutsLink(evt);

      assert.equal(keybindingsCallback.callCount, 1);
      assert.equal(keybindingsCallback.args[0][0], evt);
      assert.equal(keybindingsCallback.args[0][1], div);
      document.body.removeChild(div);
    });
  });
});
<commit_msg>Linting issues fixed<commit_after>/*
This file is part of the Juju GUI, which lets users view and manage Juju
environments within a graphical interface (https://launchpad.net/juju-gui).
Copyright (C) 2017 Canonical Ltd.

This program is free software: you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3, as published by
the Free Software Foundation.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero
General Public License for more details.

You should have received a copy of the GNU Affero General Public License along
with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

'use strict';

var juju = {components: {}}; // eslint-disable-line no-unused-vars

chai.config.includeStack = true;
chai.config.truncateThreshold = 0;

describe('HeaderHelp', function() {
  let appState;

  beforeAll(function(done) {
    // By loading this file it adds the component to the juju components.
    YUI().use('header-help', function() { done(); });
  });

  beforeEach(function() {
    appState = {
      current: {},
      changeState: sinon.stub()
    };
  });

  it('renders', function () {
    const renderer = jsTestUtils.shallowRender(
      <juju.components.HeaderHelp.prototype.wrappedComponent
        appState={appState}
        gisf={false}
        user={null} />, true);
    const output = renderer.getRenderOutput();
    const instance = renderer.getMountedInstance();

    const expected =

          <juju.components.SvgIcon name="help_16"
            className="header-menu__icon"
            size="16" />

              Help

        {''}

;

    assert.deepEqual(output, expected);
  });

  describe('menu', function () {
    const issueUrl = 'https://github.com/juju/juju-gui/issues';
    const loggedInIssueUrl = 'https://jujucharms.com/support';
    const docsUrl = 'https://jujucharms.com/docs/stable/getting-started';

    it('opens a menu when clicked', function () {
      const renderer = jsTestUtils.shallowRender(
        <juju.components.HeaderHelp.prototype.wrappedComponent
          appState={appState}
          gisf={false}
          user={null} />, true);
      const instance = renderer.getMountedInstance();
      instance.toggleHelpMenu();
      const output = renderer.getRenderOutput();

      assert.equal(output.props.children.length, 2);
      assert.deepEqual(output.props.children[0].props.className,
        'header-menu__button header-menu__show-menu');

      const expected = (<juju.components.Panel
        instanceName="header-menu__menu"
        visible={true}>

                View Documentation

              File Issue

                Keyboard shortcuts

                  Shift + ?

        </juju.components.Panel>);
      expect(output.props.children[1]).toEqualJSX(expected);
    });

    it('shows the jujuchams issues page if in gisf and logged in',
      function () {
        const renderer = jsTestUtils.shallowRender(
          <juju.components.HeaderHelp.prototype.wrappedComponent
            appState={appState}
            gisf={true}
            user={{}} />, true);
        const instance = renderer.getMountedInstance();
        instance.toggleHelpMenu();
        const output = renderer.getRenderOutput();

        assert.equal(output.props.children.length, 2);
        assert.deepEqual(output.props.children[0].props.className,
          'header-menu__button header-menu__show-menu');

        const expected = (<juju.components.Panel
          instanceName="header-menu__menu"
          visible={true}>

                  View Documentation

                Get Support

                  Keyboard shortcuts

                    Shift + ?

          </juju.components.Panel>);
        expect(output.props.children[1]).toEqualJSX(expected);
      }
    );

    it('keyboard shortcuts link calls correct keyboard shortcut cb', () => {
      const keybindingsCallback = sinon.stub();
      const keybindings = {
        'S-/': {
          target: '#shortcut-help',
          toggle: true,
          callback: keybindingsCallback
        }
      };
      const evt = {
        stopPropagation: sinon.stub()
      };
      const div = document.createElement('div');
      div.setAttribute('id', 'shortcut-help');
      document.body.appendChild(div);
      const renderer = jsTestUtils.shallowRender(
        <juju.components.HeaderHelp.prototype.wrappedComponent
          appState={appState}
          gisf={true}
          keybindings={keybindings}
          user={{}} />, true);
      const instance = renderer.getMountedInstance();
      instance._handleShortcutsLink(evt);

      assert.equal(keybindingsCallback.callCount, 1);
      assert.equal(keybindingsCallback.args[0][0], evt);
      assert.equal(keybindingsCallback.args[0][1], div);
      document.body.removeChild(div);
    });
  });
});
<|endoftext|>


<commit_before>(function() {
  'use strict';

  angular.module('nin')
    .controller('BottomCtrl', function ($scope, $interval, socket, camera, commands) {

      $scope.xScale = 1;
      $scope.xScaleTarget = 0;
      $scope.yScale = 1;

      function updateLayerBackgroundGradientStyle() {
        if(!$scope.selectedTheme) {
          return;
        }
        var wellBgColor;
        var wellBgColorAlt;
        var wellBgBorderColor;
        if($scope.selectedTheme == 'dark') {
          wellBgColor = 'rgb(52, 68, 78)';
          wellBgColorAlt = 'rgb(46, 62, 72)';
          wellBgBorderColor = 'rgb(10, 26, 36)';
        } else if($scope.selectedTheme == 'light') {
          wellBgColor = '#efefef';
          wellBgColorAlt = '#dfdfdf';
          wellBgBorderColor = '#ababab';
        }

        var beatsPerSecond = PROJECT.music.bpm / 60;
        var secondsPerBeat = 1 / beatsPerSecond;
        var framesPerSecond = 60;
        var framesPerBeat = secondsPerBeat * framesPerSecond;
        var beatWidthInPixels = framesPerBeat * $scope.xScale;
        var devicePixelRatio = window.devicePixelRatio || 1;

        var entirePatternRepeatCount = 6;
        var backgroundWidth = beatWidthInPixels * 8 * devicePixelRatio;
        var repeatedBackgroundWidth = backgroundWidth * entirePatternRepeatCount;

        var backgroundCanvas = document.createElement('canvas');
        var ctx = backgroundCanvas.getContext('2d');
        backgroundCanvas.width = repeatedBackgroundWidth;
        backgroundCanvas.height = 1;
        for(var i = 0; i < entirePatternRepeatCount; i++) {
          var entirePatternRepeatOffset = i * backgroundWidth;
          ctx.fillStyle = wellBgColor;
          ctx.fillRect(entirePatternRepeatOffset, 0,
                       backgroundWidth, 1);
          ctx.fillStyle = wellBgColorAlt;
          ctx.fillRect(entirePatternRepeatOffset + backgroundWidth / 2, 0,
                       backgroundWidth, 1);
          ctx.fillStyle = wellBgBorderColor;
          for(var j = 0; j < 8; j++) {
            ctx.fillRect(entirePatternRepeatOffset + j * backgroundWidth / 8, 0,
                         1, 1);
          }
        }
        $scope.backgroundImageDataUrl = backgroundCanvas.toDataURL();
        $scope.backgroundWidth = repeatedBackgroundWidth / devicePixelRatio;
      }

      updateLayerBackgroundGradientStyle();

      $scope.$watch('xScale', function() {
        updateLayerBackgroundGradientStyle();
      });

      $scope.$watch('selectedTheme', function() {
        updateLayerBackgroundGradientStyle();
      });

      $scope.$watch('xScaleTarget', updateXScale);
      updateXScale();
      function updateXScale() {
        var target = $('.layers-bar-container');
        var rect = target[0].getBoundingClientRect();
        var parentRect = target.parent()[0].getBoundingClientRect();
        var firstVisibleFrame = -rect.left / $scope.xScale;
        var lastVisibleFrame = (-rect.left + parentRect.width) / $scope.xScale;
        var centerFrame = firstVisibleFrame + (lastVisibleFrame - firstVisibleFrame) / 2;

        var radius = parentRect.width / 2 / $scope.xScaleTarget;
        var newFirstVisibleFrame = centerFrame - radius;
        var newLastVisibleFrame = centerFrame + radius;

        if (newFirstVisibleFrame < 0) {
          newFirstVisibleFrame = 0;
        }

        if(newLastVisibleFrame > rect.width / $scope.xScale) {
          newLastVisibleFrame = rect.width / $scope.xScale;
        }

        if(newLastVisibleFrame === 0) {
          return;
        }
        var newCenterFrame = newFirstVisibleFrame + (newLastVisibleFrame - newFirstVisibleFrame) / 2;
        var newXScaleTarget = parentRect.width / 2 / (newLastVisibleFrame - newCenterFrame) ;
        var newOffsetX = newFirstVisibleFrame * newXScaleTarget;
        $('div.bottom').parent().scrollLeft(newOffsetX);
        $scope.xScale = newXScaleTarget;
      }

      function getClickOffset($event) {
        var target = $('.layers-bar-container')[0];
        var rect = target.getBoundingClientRect();
        var offsetX = ($event.clientX - rect.left) | 0;
        var offsetY = ($event.clientY - rect.top) | 0;
        return {x: offsetX, y: offsetY};
      }

      function getScrollOffset($event) {
        var target = $('.layers-bar-container')[0];
        return target.getBoundingClientRect();
      }

      $scope.musicLayerClick = function($event) {
        $scope.demo.jumpToFrame(getClickOffset($event).x / $scope.xScale | 0);
      };

      $scope.$window = window;

      $scope.inspectLayer = function(layer) {
        $scope.$parent.$parent.inspectedLayer = $scope.inspectedLayer == layer ? null
                                                                               : layer;
        camera.startEdit(layer);
      };

      $scope.toggleMinimized = function(layer) {
        layer.minimized = !layer.minimized;
      };

      $scope.dragResizeLayer = function(event, ui, layer) {
        if (ui.position.left != (layer.startFrame * $scope.xScale | 0)) {
          socket.sendEvent('set', {
            id: layer.position,
            field: 'startFrame',
            value: (ui.position.left / $scope.xScale | 0)
          });
        } else {
          socket.sendEvent('set', {
            id: layer.position,
            field: 'endFrame',
            value: ((ui.position.left + ui.size.width) / $scope.xScale | 0)
          });
        }
      };

      $scope.loopStart = null;
      $scope.loopEnd = null;
      $scope.loopActive = false;
      commands.on('setCuePoint', function() {
        var currentBEAN = BEAN_FOR_FRAME($scope.currentFrame);
        var currentQuantizedFrame = FRAME_FOR_BEAN(currentBEAN - currentBEAN % PROJECT.music.subdivision);
        if ($scope.loopStart === null) {
          $scope.loopStart = currentQuantizedFrame;
        } else if ($scope.loopEnd === null) {
          if ($scope.loopStart > $scope.currentFrame) {
            $scope.loopEnd = currentQuantizedFrame;
            $scope.loopStart = currentQuantizedFrame;
          } else {
            $scope.loopEnd = currentQuantizedFrame;
          }
          $scope.loopActive = true;
        } else {
          $scope.loopStart = null;
          $scope.loopEnd = null;
          $scope.loopActive = false;
        }
      });

      $scope.$watch('currentFrame', function (nextFrame) {
        if ($scope.loopActive && nextFrame >= $scope.loopEnd) {
          $scope.demo.jumpToFrame($scope.loopStart);
        }
      });
    });
})();
<commit_msg>Fix initial position of x scale slider<commit_after>(function() {
  'use strict';

  angular.module('nin')
    .controller('BottomCtrl', function ($scope, $interval, socket, camera, commands) {

      $scope.xScale = 1;
      $scope.xScaleTarget = 1;
      $scope.yScale = 1;

      function updateLayerBackgroundGradientStyle() {
        if(!$scope.selectedTheme) {
          return;
        }
        var wellBgColor;
        var wellBgColorAlt;
        var wellBgBorderColor;
        if($scope.selectedTheme == 'dark') {
          wellBgColor = 'rgb(52, 68, 78)';
          wellBgColorAlt = 'rgb(46, 62, 72)';
          wellBgBorderColor = 'rgb(10, 26, 36)';
        } else if($scope.selectedTheme == 'light') {
          wellBgColor = '#efefef';
          wellBgColorAlt = '#dfdfdf';
          wellBgBorderColor = '#ababab';
        }

        var beatsPerSecond = PROJECT.music.bpm / 60;
        var secondsPerBeat = 1 / beatsPerSecond;
        var framesPerSecond = 60;
        var framesPerBeat = secondsPerBeat * framesPerSecond;
        var beatWidthInPixels = framesPerBeat * $scope.xScale;
        var devicePixelRatio = window.devicePixelRatio || 1;

        var entirePatternRepeatCount = 6;
        var backgroundWidth = beatWidthInPixels * 8 * devicePixelRatio;
        var repeatedBackgroundWidth = backgroundWidth * entirePatternRepeatCount;

        var backgroundCanvas = document.createElement('canvas');
        var ctx = backgroundCanvas.getContext('2d');
        backgroundCanvas.width = repeatedBackgroundWidth;
        backgroundCanvas.height = 1;
        for(var i = 0; i < entirePatternRepeatCount; i++) {
          var entirePatternRepeatOffset = i * backgroundWidth;
          ctx.fillStyle = wellBgColor;
          ctx.fillRect(entirePatternRepeatOffset, 0,
                       backgroundWidth, 1);
          ctx.fillStyle = wellBgColorAlt;
          ctx.fillRect(entirePatternRepeatOffset + backgroundWidth / 2, 0,
                       backgroundWidth, 1);
          ctx.fillStyle = wellBgBorderColor;
          for(var j = 0; j < 8; j++) {
            ctx.fillRect(entirePatternRepeatOffset + j * backgroundWidth / 8, 0,
                         1, 1);
          }
        }
        $scope.backgroundImageDataUrl = backgroundCanvas.toDataURL();
        $scope.backgroundWidth = repeatedBackgroundWidth / devicePixelRatio;
      }

      updateLayerBackgroundGradientStyle();

      $scope.$watch('xScale', function() {
        updateLayerBackgroundGradientStyle();
      });

      $scope.$watch('selectedTheme', function() {
        updateLayerBackgroundGradientStyle();
      });

      $scope.$watch('xScaleTarget', updateXScale);
      updateXScale();
      function updateXScale() {
        var target = $('.layers-bar-container');
        var rect = target[0].getBoundingClientRect();
        var parentRect = target.parent()[0].getBoundingClientRect();
        var firstVisibleFrame = -rect.left / $scope.xScale;
        var lastVisibleFrame = (-rect.left + parentRect.width) / $scope.xScale;
        var centerFrame = firstVisibleFrame + (lastVisibleFrame - firstVisibleFrame) / 2;

        var radius = parentRect.width / 2 / $scope.xScaleTarget;
        var newFirstVisibleFrame = centerFrame - radius;
        var newLastVisibleFrame = centerFrame + radius;

        if (newFirstVisibleFrame < 0) {
          newFirstVisibleFrame = 0;
        }

        if(newLastVisibleFrame > rect.width / $scope.xScale) {
          newLastVisibleFrame = rect.width / $scope.xScale;
        }

        if(newLastVisibleFrame === 0) {
          return;
        }
        var newCenterFrame = newFirstVisibleFrame + (newLastVisibleFrame - newFirstVisibleFrame) / 2;
        var newXScaleTarget = parentRect.width / 2 / (newLastVisibleFrame - newCenterFrame) ;
        var newOffsetX = newFirstVisibleFrame * newXScaleTarget;
        $('div.bottom').parent().scrollLeft(newOffsetX);
        $scope.xScale = newXScaleTarget;
      }

      function getClickOffset($event) {
        var target = $('.layers-bar-container')[0];
        var rect = target.getBoundingClientRect();
        var offsetX = ($event.clientX - rect.left) | 0;
        var offsetY = ($event.clientY - rect.top) | 0;
        return {x: offsetX, y: offsetY};
      }

      function getScrollOffset($event) {
        var target = $('.layers-bar-container')[0];
        return target.getBoundingClientRect();
      }

      $scope.musicLayerClick = function($event) {
        $scope.demo.jumpToFrame(getClickOffset($event).x / $scope.xScale | 0);
      };

      $scope.$window = window;

      $scope.inspectLayer = function(layer) {
        $scope.$parent.$parent.inspectedLayer = $scope.inspectedLayer == layer ? null
                                                                               : layer;
        camera.startEdit(layer);
      };

      $scope.toggleMinimized = function(layer) {
        layer.minimized = !layer.minimized;
      };

      $scope.dragResizeLayer = function(event, ui, layer) {
        if (ui.position.left != (layer.startFrame * $scope.xScale | 0)) {
          socket.sendEvent('set', {
            id: layer.position,
            field: 'startFrame',
            value: (ui.position.left / $scope.xScale | 0)
          });
        } else {
          socket.sendEvent('set', {
            id: layer.position,
            field: 'endFrame',
            value: ((ui.position.left + ui.size.width) / $scope.xScale | 0)
          });
        }
      };

      $scope.loopStart = null;
      $scope.loopEnd = null;
      $scope.loopActive = false;
      commands.on('setCuePoint', function() {
        var currentBEAN = BEAN_FOR_FRAME($scope.currentFrame);
        var currentQuantizedFrame = FRAME_FOR_BEAN(currentBEAN - currentBEAN % PROJECT.music.subdivision);
        if ($scope.loopStart === null) {
          $scope.loopStart = currentQuantizedFrame;
        } else if ($scope.loopEnd === null) {
          if ($scope.loopStart > $scope.currentFrame) {
            $scope.loopEnd = currentQuantizedFrame;
            $scope.loopStart = currentQuantizedFrame;
          } else {
            $scope.loopEnd = currentQuantizedFrame;
          }
          $scope.loopActive = true;
        } else {
          $scope.loopStart = null;
          $scope.loopEnd = null;
          $scope.loopActive = false;
        }
      });

      $scope.$watch('currentFrame', function (nextFrame) {
        if ($scope.loopActive && nextFrame >= $scope.loopEnd) {
          $scope.demo.jumpToFrame($scope.loopStart);
        }
      });
    });
})();
<|endoftext|>


<commit_before>dojo.provide("dojox.grid._data.dijitEditors");
dojo.require("dojox.grid._data.editors");
dojo.require("dijit.form.DateTextBox");
dojo.require("dijit.form.TimeTextBox");
dojo.require("dijit.form.ComboBox");
dojo.require("dijit.form.CheckBox");
dojo.require("dijit.form.TextBox");
dojo.require("dijit.form.NumberSpinner");
dojo.require("dijit.form.NumberTextBox");
dojo.require("dijit.form.CurrencyTextBox");
dojo.require("dijit.form.Slider");
dojo.require("dijit.Editor");

dojo.declare("dojox.grid.editors.Dijit", dojox.grid.editors.base, {
	editorClass: "dijit.form.TextBox",
	constructor: function(inCell){
		this.editor = null;
		this.editorClass = dojo.getObject(this.cell.editorClass || this.editorClass);
	},
	format: function(inDatum, inRowIndex){
		this.needFormatNode(inDatum, inRowIndex);
		return "

";
	},
	getValue: function(inRowIndex){
		return this.editor.getValue();
	},
	setValue: function(inRowIndex, inValue){
		if(this.editor&&this.editor.setValue){
			this.editor.setValue(inValue);
		}else{
			this.inherited(arguments);
		}
	},
	getEditorProps: function(inDatum){
		return dojo.mixin({}, this.cell.editorProps||{}, {
			constraints: dojo.mixin({}, this.cell.constraint) || {}, //TODO: really just for ValidationTextBoxes
			value: inDatum
		});
	},
	createEditor: function(inNode, inDatum, inRowIndex){
		return new this.editorClass(this.getEditorProps(inDatum), inNode);

	},
	attachEditor: function(inNode, inDatum, inRowIndex){
		inNode.appendChild(this.editor.domNode);
		this.setValue(inRowIndex, inDatum);
	},
	formatNode: function(inNode, inDatum, inRowIndex){
		if(!this.editorClass){
			return inDatum;
		}
		if(!this.editor){
			this.editor = this.createEditor.apply(this, arguments);
		}else{
			this.attachEditor.apply(this, arguments);
		}
		this.sizeEditor.apply(this, arguments);
		this.cell.grid.rowHeightChanged(inRowIndex);
		this.focus();
	},
	sizeEditor: function(inNode, inDatum, inRowIndex){
		var
			p = this.cell.getNode(inRowIndex),
			box = dojo.contentBox(p);
		dojo.marginBox(this.editor.domNode, {w: box.w});
	},
	focus: function(inRowIndex, inNode){
		if(this.editor){
			setTimeout(dojo.hitch(this.editor, function(){
				dojox.grid.fire(this, "focus");
			}), 0);
		}
	},
	_finish: function(inRowIndex){
		this.inherited(arguments);
		dojox.grid.removeNode(this.editor.domNode);
	}
});

dojo.declare("dojox.grid.editors.ComboBox", dojox.grid.editors.Dijit, {
	editorClass: "dijit.form.ComboBox",
	getEditorProps: function(inDatum){
		var items=[];
		dojo.forEach(this.cell.options, function(o){
			items.push({name: o, value: o});
		});
		var store = new dojo.data.ItemFileReadStore({data: {identifier:"name", items: items}});
		return dojo.mixin({}, this.cell.editorProps||{}, {
			value: inDatum,
			store: store
		});
	},
	getValue: function(){
		var e = this.editor;
		// make sure to apply the displayed value
		e.setDisplayedValue(e.getDisplayedValue());
		return e.getValue();
	}
});

dojo.declare("dojox.grid.editors.DateTextBox", dojox.grid.editors.Dijit, {
	editorClass: "dijit.form.DateTextBox",
	setValue: function(inRowIndex, inValue){
		if(this.editor){
			this.editor.setValue(new Date(inValue));
		}else{
			this.inherited(arguments);
		}
	},
	getEditorProps: function(inDatum){
		return dojo.mixin(this.inherited(arguments), {
			value: new Date(inDatum)
		});
	}
});

dojo.declare("dojox.grid.editors.CheckBox", dojox.grid.editors.Dijit, {
	editorClass: "dijit.form.CheckBox",
	getValue: function(){
		return this.editor.checked;
	}
});

dojo.declare("dojox.grid.editors.Editor", dojox.grid.editors.Dijit, {
	editorClass: "dijit.Editor",
	getEditorProps: function(inDatum){
		return dojo.mixin({}, this.cell.editorProps||{}, {
			height: this.cell.editorHeight || "100px"
		});
	},
	createEditor: function(inNode, inDatum, inRowIndex){
		// editor needs its value set after creation
		var editor = new this.editorClass(this.getEditorProps(inDatum), inNode);
		editor.setValue(inDatum);
		return editor;
	},
	formatNode: function(inNode, inDatum, inRowIndex){
		this.inherited(arguments);
		// FIXME: seem to need to reopen the editor and display the toolbar
		var e = this.editor;
		e.open();
		if(this.cell.editorToolbar){
			dojo.place(e.toolbar.domNode, e.editingArea, "before");
		}
	}
});<commit_msg>!strict minor updates to grid dijit combobox and checkbox editor to fix grid/tests/test_edit_dijit.html<commit_after>dojo.provide("dojox.grid._data.dijitEditors");
dojo.require("dojox.grid._data.editors");
dojo.require("dijit.form.DateTextBox");
dojo.require("dijit.form.TimeTextBox");
dojo.require("dijit.form.ComboBox");
dojo.require("dojo.data.ItemFileReadStore");
dojo.require("dijit.form.CheckBox");
dojo.require("dijit.form.TextBox");
dojo.require("dijit.form.NumberSpinner");
dojo.require("dijit.form.NumberTextBox");
dojo.require("dijit.form.CurrencyTextBox");
dojo.require("dijit.form.Slider");
dojo.require("dijit.Editor");

dojo.declare("dojox.grid.editors.Dijit", dojox.grid.editors.base, {
	editorClass: "dijit.form.TextBox",
	constructor: function(inCell){
		this.editor = null;
		this.editorClass = dojo.getObject(this.cell.editorClass || this.editorClass);
	},
	format: function(inDatum, inRowIndex){
		this.needFormatNode(inDatum, inRowIndex);
		return "

";
	},
	getValue: function(inRowIndex){
		return this.editor.getValue();
	},
	setValue: function(inRowIndex, inValue){
		if(this.editor&&this.editor.setValue){
			this.editor.setValue(inValue);
		}else{
			this.inherited(arguments);
		}
	},
	getEditorProps: function(inDatum){
		return dojo.mixin({}, this.cell.editorProps||{}, {
			constraints: dojo.mixin({}, this.cell.constraint) || {}, //TODO: really just for ValidationTextBoxes
			value: inDatum
		});
	},
	createEditor: function(inNode, inDatum, inRowIndex){
		return new this.editorClass(this.getEditorProps(inDatum), inNode);

	},
	attachEditor: function(inNode, inDatum, inRowIndex){
		inNode.appendChild(this.editor.domNode);
		this.setValue(inRowIndex, inDatum);
	},
	formatNode: function(inNode, inDatum, inRowIndex){
		if(!this.editorClass){
			return inDatum;
		}
		if(!this.editor){
			this.editor = this.createEditor.apply(this, arguments);
		}else{
			this.attachEditor.apply(this, arguments);
		}
		this.sizeEditor.apply(this, arguments);
		this.cell.grid.rowHeightChanged(inRowIndex);
		this.focus();
	},
	sizeEditor: function(inNode, inDatum, inRowIndex){
		var
			p = this.cell.getNode(inRowIndex),
			box = dojo.contentBox(p);
		dojo.marginBox(this.editor.domNode, {w: box.w});
	},
	focus: function(inRowIndex, inNode){
		if(this.editor){
			setTimeout(dojo.hitch(this.editor, function(){
				dojox.grid.fire(this, "focus");
			}), 0);
		}
	},
	_finish: function(inRowIndex){
		this.inherited(arguments);
		dojox.grid.removeNode(this.editor.domNode);
	}
});

dojo.declare("dojox.grid.editors.ComboBox", dojox.grid.editors.Dijit, {
	editorClass: "dijit.form.ComboBox",
	getEditorProps: function(inDatum){
		var items=[];
		dojo.forEach(this.cell.options, function(o){
			items.push({name: o, value: o});
		});
		var store = new dojo.data.ItemFileReadStore({data: {identifier:"name", items: items}});
		return dojo.mixin({}, this.cell.editorProps||{}, {
			value: inDatum,
			store: store
		});
	},
	getValue: function(){
		var e = this.editor;
		// make sure to apply the displayed value
		e.setDisplayedValue(e.getDisplayedValue());
		return e.getValue();
	}
});

dojo.declare("dojox.grid.editors.DateTextBox", dojox.grid.editors.Dijit, {
	editorClass: "dijit.form.DateTextBox",
	setValue: function(inRowIndex, inValue){
		if(this.editor){
			this.editor.setValue(new Date(inValue));
		}else{
			this.inherited(arguments);
		}
	},
	getEditorProps: function(inDatum){
		return dojo.mixin(this.inherited(arguments), {
			value: new Date(inDatum)
		});
	}
});

dojo.declare("dojox.grid.editors.CheckBox", dojox.grid.editors.Dijit, {
	editorClass: "dijit.form.CheckBox",
	getValue: function(){
		return this.editor.checked;
	},
	setValue: function(inRowIndex, inValue){
		if(this.editor&&this.editor.setAttribute){
			this.editor.setAttribute("checked", inValue);
		}else{
			this.inherited(arguments);
		}
	},
	sizeEditor: function(inNode, inDatum, inRowIndex){
		return;
	}
});

dojo.declare("dojox.grid.editors.Editor", dojox.grid.editors.Dijit, {
	editorClass: "dijit.Editor",
	getEditorProps: function(inDatum){
		return dojo.mixin({}, this.cell.editorProps||{}, {
			height: this.cell.editorHeight || "100px"
		});
	},
	createEditor: function(inNode, inDatum, inRowIndex){
		// editor needs its value set after creation
		var editor = new this.editorClass(this.getEditorProps(inDatum), inNode);
		editor.setValue(inDatum);
		return editor;
	},
	formatNode: function(inNode, inDatum, inRowIndex){
		this.inherited(arguments);
		// FIXME: seem to need to reopen the editor and display the toolbar
		var e = this.editor;
		e.open();
		if(this.cell.editorToolbar){
			dojo.place(e.toolbar.domNode, e.editingArea, "before");
		}
	}
});<|endoftext|>


<commit_before>import React, {Component} from 'react'
import Icon from 'react-toolbox/lib/font_icon/FontIcon'
import List from 'react-toolbox/lib/list/List'
import ListItem from 'react-toolbox/lib/list/ListItem'
import Tooltip from 'react-toolbox/lib/tooltip'
import Button from 'react-toolbox/lib/button/Button'
import ConfirmDeleteButton from '../components/ConfirmDeleteButton'
import { Link } from 'react-router'
import { connect } from 'react-redux'
import { fetchUsersPage, deleteUser, getUsersPage } from '../reducers/users.reducer'
import PaginationFooter from '../components/PaginationFooter'

const TooltipIcon = Tooltip(Icon);
const TooltipButton = Tooltip(Button);

class Users extends Component {
  pageSize = 5;
  componentDidMount() {
    this.fetchPage(0);
  }
  fetchPage(page) {
    this.props.fetchUsersPage(page, this.pageSize);
  }
  deleteUser = (user) => {
    this.props.deleteUser(user);
  }
  renderListActions(user) {
    const actionsData = [
      {link: `/users/${user.id}`, icon: 'edit', tooltip: 'Editar'}
    ]
    const actions = actionsData.map((action, i) => (
      <Link
        to={action.link}
        key={`action${i}_user${user.id}`}
        style={{color: '#757575'}} >
        <TooltipIcon
          tooltip={action.tooltip}
          value={action.icon} />
      </Link>
    ))
    actions.push((
      <ConfirmDeleteButton
        tooltip="Borrar usuario"
        title={`Borrar usuario ${user.name}`}
        key={`delete_user${user.id}`}
        onDelete={() => this.deleteUser(user)}
      />
    ))
    return actions;
  }
  render() {
    const {loading, error, children, pageParams} = this.props;
    const users = this.props.users || [];
    return (

Usuarios

          {loading &&
Cargando ...
}
          {error &&
{error}
}

        <Link to="/users/new">
          <TooltipButton
            icon="add"
            floating accent
            tooltip="Nuevo usuario"
            tooltipPosition="left"
            className="list-corner-fab"
          />
        </Link>
        <List className="list">
          {users.map((user, i) => (
            <Link
              key={i}
              className="link-reset"
              to={`/users/${user.id}`}
            >
              <ListItem
                caption={user.name}
                leftIcon="work"
                className="list-item"
                rightActions={this.renderListActions(user)}
              />
            </Link>
          ))}
        </List>
        <PaginationFooter
          params={pageParams}
          onPageChange={page => this.fetchPage(page)}
        />
        {children}

    )
  }
}

const mapStateToProps = state => {
  const {items, loading, params} = getUsersPage(state)
  return {
    pageParams: params,
    users: items,
    loading
  }
}
const actions = {fetchUsersPage, deleteUser}

export default connect(mapStateToProps, actions)(Users);
<commit_msg>refactor: user list<commit_after>import React, {Component} from 'react'
import Icon from 'react-toolbox/lib/font_icon/FontIcon'
import List from 'react-toolbox/lib/list/List'
import ListItem from 'react-toolbox/lib/list/ListItem'
import Tooltip from 'react-toolbox/lib/tooltip'
import Button from 'react-toolbox/lib/button/Button'
import ConfirmDeleteButton from '../components/ConfirmDeleteButton'
import { Link } from 'react-router'
import { connect } from 'react-redux'
import { fetchUsersPage, deleteUser, getUsersPage } from '../reducers/users.reducer'
import PaginationFooter from '../components/PaginationFooter'

const TooltipIcon = Tooltip(Icon);
const TooltipButton = Tooltip(Button);

class Users extends Component {
  pageSize = 5;
  componentDidMount() {
    this.fetchPage(0);
  }
  fetchPage(page) {
    this.props.fetchUsersPage(page, this.pageSize);
  }
  renderListActions(user) {
    return [
      <Link
        to={`/users/${user.id}`}
        key={`edit_user_${user.id}`}
        style={{color: '#757575'}} >
        <TooltipIcon tooltip="Editar" value="edit" />
      </Link>,
      <ConfirmDeleteButton
        tooltip="Borrar"
        title={`Borrar usuario ${user.name}`}
        key={`delete_user${user.id}`}
        onDelete={() => this.props.deleteUser(user)}
      />
    ]
  }
  render() {
    const {loading, children, pageParams} = this.props;
    const users = this.props.users || [];
    return (

Usuarios

          {loading &&
Cargando ...
}

        <Link to="/users/new">
          <TooltipButton
            icon="add"
            floating accent
            tooltip="Nuevo usuario"
            tooltipPosition="left"
            className="list-corner-fab"
          />
        </Link>
        <List className="list">
          {users.map((user, i) => (
            <Link key={i} className="link-reset"
                  to={`/users/${user.id}`}>
              <ListItem
                caption={user.name}
                leftIcon="work"
                className="list-item"
                rightActions={this.renderListActions(user)}
              />
            </Link>
          ))}
        </List>
        <PaginationFooter
          params={pageParams}
          onPageChange={page => this.fetchPage(page)}
        />
        {children}

    )
  }
}

const mapStateToProps = state => {
  const {items, loading, params} = getUsersPage(state)
  return {
    pageParams: params,
    users: items,
    loading
  }
}
const actions = {fetchUsersPage, deleteUser}

export default connect(mapStateToProps, actions)(Users);
<|endoftext|>


<commit_before>/*
Copyright 2016 - 2018 by Jan Dockx

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

'use strict'

const must = require('must')
const os = require('os')

// noinspection FunctionNamingConventionJS
function x () {
  if (arguments.length <= 0) {
    return []
  }
  return Array.prototype.reduce.call(
    arguments,
    (acc, arrayI) => {
      const ret = []
      acc.forEach(elementSoFar => { arrayI.forEach(elementOfI => { ret.push(elementSoFar.concat([elementOfI])) }) })
      return ret
    },
    [[]]
  )
}

function expectOwnFrozenProperty (subject, propertyName) {
  const propertyDescriptor = Object.getOwnPropertyDescriptor(subject, propertyName)
  propertyDescriptor.must.be.truthy()
  propertyDescriptor.enumerable.must.be.true()
  propertyDescriptor.configurable.must.be.false()
  propertyDescriptor.writable.must.be.false()
  const failFunction = function () {
    // noinspection MagicNumberJS
    subject[propertyName] = 42 + ' some outlandish other value'
  }
  failFunction.must.throw(TypeError)
}

// noinspection FunctionNamingConventionJS
function prototypeThatHasOwnPropertyDescriptor (subject, propertyName) {
  if (!subject) {
    return subject
  }
  if (Object.getOwnPropertyDescriptor(subject, propertyName)) {
    return subject
  }
  return prototypeThatHasOwnPropertyDescriptor(Object.getPrototypeOf(subject), propertyName)
}

// noinspection FunctionNamingConventionJS
function expectDerivedPropertyOnAPrototype (subject, propertyName, configurable) {
  const prototype = prototypeThatHasOwnPropertyDescriptor(subject, propertyName)
  const propertyDescriptor = Object.getOwnPropertyDescriptor(prototype, propertyName)
  propertyDescriptor.must.be.truthy()
  propertyDescriptor.enumerable.must.be.true()
  propertyDescriptor.configurable.must.equal(configurable)
  propertyDescriptor.must.not.have.property('writable')
  propertyDescriptor.get.must.be.a.function()
  must(propertyDescriptor.set).be.falsy()
}

// noinspection FunctionNamingConventionJS
function expectConfigurableDerivedPropertyOnAPrototype (subject, propertyName) {
  expectDerivedPropertyOnAPrototype(subject, propertyName, true)
}

// noinspection FunctionNamingConventionJS
function expectFrozenDerivedPropertyOnAPrototype (subject, propertyName) {
  expectDerivedPropertyOnAPrototype(subject, propertyName, false)
}

function expectFrozenPropertyOnAPrototype (subject, propertyName) {
  const prototype = prototypeThatHasOwnPropertyDescriptor(subject, propertyName)
  expectOwnFrozenProperty(prototype, propertyName)
}

// noinspection FunctionNamingConventionJS
function expectFrozenReadOnlyArrayPropertyWithPrivateBackingField (subject, propName, privatePropName) {
  subject.must.have.ownProperty(privatePropName) // array not shared
  subject[privatePropName].must.be.an.array()
  this.expectOwnFrozenProperty(subject, privatePropName)
  subject[propName].must.be.an.array()
  this.expectFrozenDerivedPropertyOnAPrototype(subject, propName)
  const failFunction = function () {
    // noinspection MagicNumberJS
    subject[propName] = 42 + ' some outlandish other value'
  }
  failFunction.must.throw(TypeError)
}

function expectToBeArrayOfFunctions (a) {
  a.must.be.an.array()
  a.forEach(element => { element.must.be.a.function() })
}

const doLog = false

// noinspection FunctionNamingConventionJS
function log () {
  if (doLog) {
    console.log.apply(undefined, arguments)
    console.log()
  }
}

function showStack (exc) {
  log('Exception stack%s---------------%s%s', os.EOL, os.EOL, exc.stack)
}

function regExpEscape (s) {
  // http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript
  return s.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&')
}

function propertyIsWritable (object, propertyName) {
  const prototype = prototypeThatHasOwnPropertyDescriptor(object, propertyName)
  const pd = prototype && Object.getOwnPropertyDescriptor(prototype, propertyName)
  return !pd || pd.writable
}

function anyCasesGenerators (discriminator) {
  // noinspection JSPrimitiveTypeWrapperUsage,MagicNumberJS
  const generators = [
    () => new Error('This is a ' + discriminator + ' case'),
    () => undefined,
    () => null,
    () => 1,
    () => 0,
    () => 'a string that is used as a ' + discriminator,
    () => '',
    () => true,
    () => false,
    () => new Date(),
    () => /foo/,
    () => function () { return 'this simulates a ' + discriminator },
    // eslint-disable-next-line
    () => new Number(42),
    // eslint-disable-next-line
    () => new Boolean(false),
    // eslint-disable-next-line
    () => new String(discriminator + ' string'),
    () => arguments,
    () => ({}),
    () => ({a: 1, b: 'b', c: {}, d: {d1: undefined, d2: 'd2', d3: {d31: 31}}}),
    () => []
  ]
  const result = generators.slice()
  result.push(() => generators.map(g => g()))
  return result
}

// http://stackoverflow.com/questions/9847580/how-to-detect-safari-chrome-ie-firefox-and-opera-browser
// noinspection OverlyComplexFunctionJS
function environment () {
  // eslint-disable-next-line
  if ((new Function('try {return this === global;}catch(e){return false;}'))()) {
    console.log('Node (no User Agent')
    return 'node'
  }
  // noinspection JSUnresolvedVariable
  const ua = navigator.userAgent
  console.log(`User Agent: "${ua}"`)
  // noinspection JSUnresolvedVariable
  if (
    // eslint-disable-next-line
    (!!window.opr && !!opr.addons) ||
    !!window.opera ||
    navigator.userAgent.indexOf(' OPR/') >= 0
  ) {
    return 'opera'
  }
  // noinspection JSUnresolvedVariable
  if (typeof InstallTrigger !== 'undefined') {
    return 'firefox'
  }
  // this no longer detects safari in v11
  // noinspection JSUnresolvedVariable
  if (/constructor/i.test(window.HTMLElement) ||
      (function (p) {
        return p.toString() === '[object SafariRemoteNotification]'
      })(
        // eslint-disable-next-line
        !window['safari'] || safari.pushNotification
      )) {
    return 'safari'
  }
  // noinspection PointlessBooleanExpressionJS,JSUnresolvedVariable
  if (/* @cc_on!@ */false || !!document.documentMode) {
    return 'ie'
  }
  // noinspection JSUnresolvedVariable
  if (window.StyleMedia) {
    return 'edge'
  }
  // noinspection JSUnresolvedVariable
  if (!!window.chrome && !!window.chrome.webstore) {
    return 'chrome'
  }
  // noinspection JSUnresolvedVariable
  if (window.CSS) {
    if (ua.indexOf('HeadlessChrome')) {
      return 'headless-chrome'
    }
    if (ua.indexOf('Edge') >= 0) {
      return 'edge'
    }
    if (ua.indexOf('Safari') >= 0) {
      return 'safari'
    }
    return 'blink'
  }
  // eslint-disable-next-line
  if ((new Function('try {return this === window;}catch(e){ return false;}'))()) {
    return 'browser'
  }
  return undefined
}

function trimLineAndColumnPattern (stackLine) {
  return stackLine
    // node, chrome
    .replace(/:\d*:\d*\)$/, ')')
    // other browsers
    .replace(/:\d*:\d*$/, '')
}

function mustBeCallerLocation (actual, expected) {
  expected.must.be.a.string()
  trimLineAndColumnPattern(expected).must.equal(trimLineAndColumnPattern(actual))
}

const env = environment()
console.log(`Detected environment "${env}"`)

module.exports = {
  x: x,
  expectOwnFrozenProperty: expectOwnFrozenProperty,
  expectConfigurableDerivedPropertyOnAPrototype: expectConfigurableDerivedPropertyOnAPrototype,
  expectFrozenDerivedPropertyOnAPrototype: expectFrozenDerivedPropertyOnAPrototype,
  expectFrozenReadOnlyArrayPropertyWithPrivateBackingField: expectFrozenReadOnlyArrayPropertyWithPrivateBackingField,
  expectFrozenPropertyOnAPrototype: expectFrozenPropertyOnAPrototype,
  expectToBeArrayOfFunctions: expectToBeArrayOfFunctions,
  log: log,
  showStack: showStack,
  regExpEscape: regExpEscape,
  propertyIsWritable: propertyIsWritable,
  anyCasesGenerators: anyCasesGenerators,
  environment: env,
  mustBeCallerLocation: mustBeCallerLocation
}
<commit_msg>Fix determination of headless chrome<commit_after>/*
Copyright 2016 - 2018 by Jan Dockx

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

'use strict'

const must = require('must')
const os = require('os')

// noinspection FunctionNamingConventionJS
function x () {
  if (arguments.length <= 0) {
    return []
  }
  return Array.prototype.reduce.call(
    arguments,
    (acc, arrayI) => {
      const ret = []
      acc.forEach(elementSoFar => { arrayI.forEach(elementOfI => { ret.push(elementSoFar.concat([elementOfI])) }) })
      return ret
    },
    [[]]
  )
}

function expectOwnFrozenProperty (subject, propertyName) {
  const propertyDescriptor = Object.getOwnPropertyDescriptor(subject, propertyName)
  propertyDescriptor.must.be.truthy()
  propertyDescriptor.enumerable.must.be.true()
  propertyDescriptor.configurable.must.be.false()
  propertyDescriptor.writable.must.be.false()
  const failFunction = function () {
    // noinspection MagicNumberJS
    subject[propertyName] = 42 + ' some outlandish other value'
  }
  failFunction.must.throw(TypeError)
}

// noinspection FunctionNamingConventionJS
function prototypeThatHasOwnPropertyDescriptor (subject, propertyName) {
  if (!subject) {
    return subject
  }
  if (Object.getOwnPropertyDescriptor(subject, propertyName)) {
    return subject
  }
  return prototypeThatHasOwnPropertyDescriptor(Object.getPrototypeOf(subject), propertyName)
}

// noinspection FunctionNamingConventionJS
function expectDerivedPropertyOnAPrototype (subject, propertyName, configurable) {
  const prototype = prototypeThatHasOwnPropertyDescriptor(subject, propertyName)
  const propertyDescriptor = Object.getOwnPropertyDescriptor(prototype, propertyName)
  propertyDescriptor.must.be.truthy()
  propertyDescriptor.enumerable.must.be.true()
  propertyDescriptor.configurable.must.equal(configurable)
  propertyDescriptor.must.not.have.property('writable')
  propertyDescriptor.get.must.be.a.function()
  must(propertyDescriptor.set).be.falsy()
}

// noinspection FunctionNamingConventionJS
function expectConfigurableDerivedPropertyOnAPrototype (subject, propertyName) {
  expectDerivedPropertyOnAPrototype(subject, propertyName, true)
}

// noinspection FunctionNamingConventionJS
function expectFrozenDerivedPropertyOnAPrototype (subject, propertyName) {
  expectDerivedPropertyOnAPrototype(subject, propertyName, false)
}

function expectFrozenPropertyOnAPrototype (subject, propertyName) {
  const prototype = prototypeThatHasOwnPropertyDescriptor(subject, propertyName)
  expectOwnFrozenProperty(prototype, propertyName)
}

// noinspection FunctionNamingConventionJS
function expectFrozenReadOnlyArrayPropertyWithPrivateBackingField (subject, propName, privatePropName) {
  subject.must.have.ownProperty(privatePropName) // array not shared
  subject[privatePropName].must.be.an.array()
  this.expectOwnFrozenProperty(subject, privatePropName)
  subject[propName].must.be.an.array()
  this.expectFrozenDerivedPropertyOnAPrototype(subject, propName)
  const failFunction = function () {
    // noinspection MagicNumberJS
    subject[propName] = 42 + ' some outlandish other value'
  }
  failFunction.must.throw(TypeError)
}

function expectToBeArrayOfFunctions (a) {
  a.must.be.an.array()
  a.forEach(element => { element.must.be.a.function() })
}

const doLog = false

// noinspection FunctionNamingConventionJS
function log () {
  if (doLog) {
    console.log.apply(undefined, arguments)
    console.log()
  }
}

function showStack (exc) {
  log('Exception stack%s---------------%s%s', os.EOL, os.EOL, exc.stack)
}

function regExpEscape (s) {
  // http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript
  return s.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&')
}

function propertyIsWritable (object, propertyName) {
  const prototype = prototypeThatHasOwnPropertyDescriptor(object, propertyName)
  const pd = prototype && Object.getOwnPropertyDescriptor(prototype, propertyName)
  return !pd || pd.writable
}

function anyCasesGenerators (discriminator) {
  // noinspection JSPrimitiveTypeWrapperUsage,MagicNumberJS
  const generators = [
    () => new Error('This is a ' + discriminator + ' case'),
    () => undefined,
    () => null,
    () => 1,
    () => 0,
    () => 'a string that is used as a ' + discriminator,
    () => '',
    () => true,
    () => false,
    () => new Date(),
    () => /foo/,
    () => function () { return 'this simulates a ' + discriminator },
    // eslint-disable-next-line
    () => new Number(42),
    // eslint-disable-next-line
    () => new Boolean(false),
    // eslint-disable-next-line
    () => new String(discriminator + ' string'),
    () => arguments,
    () => ({}),
    () => ({a: 1, b: 'b', c: {}, d: {d1: undefined, d2: 'd2', d3: {d31: 31}}}),
    () => []
  ]
  const result = generators.slice()
  result.push(() => generators.map(g => g()))
  return result
}

// http://stackoverflow.com/questions/9847580/how-to-detect-safari-chrome-ie-firefox-and-opera-browser
// noinspection OverlyComplexFunctionJS
function environment () {
  // eslint-disable-next-line
  if ((new Function('try {return this === global;}catch(e){return false;}'))()) {
    console.log('Node (no User Agent')
    return 'node'
  }
  // noinspection JSUnresolvedVariable
  const ua = navigator.userAgent
  console.log(`User Agent: "${ua}"`)
  // noinspection JSUnresolvedVariable
  if (
    // eslint-disable-next-line
    (!!window.opr && !!opr.addons) ||
    !!window.opera ||
    navigator.userAgent.indexOf(' OPR/') >= 0
  ) {
    return 'opera'
  }
  // noinspection JSUnresolvedVariable
  if (typeof InstallTrigger !== 'undefined') {
    return 'firefox'
  }
  // this no longer detects safari in v11
  // noinspection JSUnresolvedVariable
  if (/constructor/i.test(window.HTMLElement) ||
      (function (p) {
        return p.toString() === '[object SafariRemoteNotification]'
      })(
        // eslint-disable-next-line
        !window['safari'] || safari.pushNotification
      )) {
    return 'safari'
  }
  // noinspection PointlessBooleanExpressionJS,JSUnresolvedVariable
  if (/* @cc_on!@ */false || !!document.documentMode) {
    return 'ie'
  }
  // noinspection JSUnresolvedVariable
  if (window.StyleMedia) {
    return 'edge'
  }
  // noinspection JSUnresolvedVariable
  if (!!window.chrome && !!window.chrome.webstore) {
    return 'chrome'
  }
  // noinspection JSUnresolvedVariable
  if (window.CSS) {
    if (ua.indexOf('HeadlessChrome') >= 0) {
      return 'headless-chrome'
    }
    if (ua.indexOf('Edge') >= 0) {
      return 'edge'
    }
    if (ua.indexOf('Safari') >= 0) {
      return 'safari'
    }
    return 'blink'
  }
  // eslint-disable-next-line
  if ((new Function('try {return this === window;}catch(e){ return false;}'))()) {
    return 'browser'
  }
  return undefined
}

function trimLineAndColumnPattern (stackLine) {
  return stackLine
    // node, chrome
    .replace(/:\d*:\d*\)$/, ')')
    // other browsers
    .replace(/:\d*:\d*$/, '')
}

function mustBeCallerLocation (actual, expected) {
  expected.must.be.a.string()
  trimLineAndColumnPattern(expected).must.equal(trimLineAndColumnPattern(actual))
}

const env = environment()
console.log(`Detected environment "${env}"`)

module.exports = {
  x: x,
  expectOwnFrozenProperty: expectOwnFrozenProperty,
  expectConfigurableDerivedPropertyOnAPrototype: expectConfigurableDerivedPropertyOnAPrototype,
  expectFrozenDerivedPropertyOnAPrototype: expectFrozenDerivedPropertyOnAPrototype,
  expectFrozenReadOnlyArrayPropertyWithPrivateBackingField: expectFrozenReadOnlyArrayPropertyWithPrivateBackingField,
  expectFrozenPropertyOnAPrototype: expectFrozenPropertyOnAPrototype,
  expectToBeArrayOfFunctions: expectToBeArrayOfFunctions,
  log: log,
  showStack: showStack,
  regExpEscape: regExpEscape,
  propertyIsWritable: propertyIsWritable,
  anyCasesGenerators: anyCasesGenerators,
  environment: env,
  mustBeCallerLocation: mustBeCallerLocation
}
<|endoftext|>


<commit_before>import {join} from "path"

import Schema from "parasprite"
import invariant from "@octetstream/invariant"
import isPlainObject from "lodash/isPlainObject"
import isEmpty from "lodash/isEmpty"
import rd from "require-dir"

import objectIterator from "core/helper/iterator/sync/objectIterator"

const SCHEMA_ROOT = join(process.cwd(), "graphql/schema")

/**
 * Set resolver from config
 *
 * @param {parasprite.Type} t – one of schema root type or GraphQLObjectType
 * @param {string} name
 * @param {object} config
 *
 * @return parasprite.Type
 */
function setResolver(t, name, config) {
  const {resolve, args} = config

  if (resolve && !isPlainObject(resolve)) {
    throw new TypeError(
      "Resolvers are allowed only as configuration object. " +
      `Check out the "${name}" resolve declaration.`
    )
  }

  t = t.resolve({...resolve, name})

  for (const [key, arg] of objectIterator.entries(args)) {
    if (!isPlainObject(arg)) {
      throw new TypeError(
        "Arguments are allowed only as configuration object. " +
        `Check out the "${key}" argument declaration for "${name}" resolver.`
      )
    }

    t.arg(key, arg.type, arg.required) // set argument to resolver
  }

  return t.end()
}

/**
 * Add resolvers to given type of app schema
 *
 * Note that you can ignore each of these resolver by adding .__ignore suffix
 *   to a filename or "ignore" (should be set to "true") field in a config.
 *
 * @param {parasprite.Type} t – one of schema root type or GraphQLObjectType
 *
 * @return {function}
 */
function setResolvers(t, obj) {
  for (const [name, resolver] of objectIterator.entries(obj)) {
    if (!(/\.__ignore__/.test(name) || resolver.ignore === true)) {
      setResolver(t, name, resolver)
    }
  }

  return t.end()
}

/**
 * Describe GraphQL schema using parasprite chainable API
 *
 * @return {graphql.GraphQLSchema}
 */
function makeSchema() {
  const resolvers = rd(SCHEMA_ROOT, {
    recurse: true
  })

  // Make parasprite.Schema instance to describe GraphQL schema for the app.
  let schema = Schema()

  invariant(
    isEmpty(resolvers.query), TypeError,
    "Query field is required for GraphQL schema."
  )

  schema = setResolvers(
    schema.query(
      "Shelves",
      "A Shelves type provides the root fields with available query resolvers."
    ),

    resolvers.query
  )

  // Add mutation resolvers if they exists
  if (!isEmpty(resolvers.mutation)) {
    schema = setResolvers(
      schema.mutation(
        "Spells",
        "A Spells type provides the root fields " +
        "with available mutation resolvers."
      ),

      resolvers.mutation
    )
  }

  // Add subscription resolvers if they exists
  if (!isEmpty(resolvers.subscription)) {
    schema = setResolvers(
      schema.subscription(
        "MagicFlow",
        "A MagicFlow type provides the root fields " +
        "with available subscription resolvers."
      ),

      resolvers.subscription
    )
  }

  return schema.end()
}

export default makeSchema()
<commit_msg>One more improvement for GraphQL schema collector.<commit_after>import {join} from "path"

import {isType as isGraphQLType} from "graphql"

import Schema from "parasprite"
import invariant from "@octetstream/invariant"
import isPlainObject from "lodash/isPlainObject"
import isEmpty from "lodash/isEmpty"
import rd from "require-dir"

import objectIterator from "core/helper/iterator/sync/objectIterator"
import getType from "core/helper/util/getType"

const isArray = Array.isArray

const SCHEMA_ROOT = join(process.cwd(), "graphql/schema")

function setArgs(t, args, resolver) {
  invariant(
    !isPlainObject(args), TypeError,
    "Resolver arguments should be exported as plain object. Received %s",
    getType(args)
  )

  for (const entry of objectIterator.entries(args)) {
    const name = entry[0]
    let arg = entry[1]

    if (isGraphQLType(arg)) {
      arg = {
        type: arg,
        required: false
      }
    } else if (isArray(arg) && isGraphQLType(arg[0])) {
      arg = {
        type: arg[0],
        required: Boolean(arg[1])
      }
    } else if (!(isEmpty(arg) || isPlainObject(arg))) {
      invariant(
        true, TypeError,
        "Argument configuration is required if argument has been declared. " +
        "Allowed formats: any GraphQL type, an array or plain object. " +
        "Received %s. Check out a declaration of %s argument in %s resolver.",
        getType(arg), name, resolver
      )
    }

    t.arg(name, arg.type, arg.required)
  }
}

/**
 * Set resolver from config
 *
 * @param {parasprite.Type} t – one of schema root type or GraphQLObjectType
 * @param {string} name
 * @param {object} config
 *
 * @return parasprite.Type
 */
function setResolver(t, name, config) {
  const {resolve, args} = config

  invariant(
    (resolve && !isPlainObject(resolve)), TypeError,
    "Resolvers are allowed only as configuration object. " +
    "Check out the \"%s\" resolve declaration.", name
  )

  t = t.resolve({...resolve, name})

  if (!isEmpty(args)) {
    setArgs(t, args, name)
  }

  return t.end()
}

/**
 * Add resolvers to given type of app schema
 *
 * Note that you can ignore each of these resolver by adding .__ignore suffix
 *   to a filename or "ignore" (should be set to "true") field in a config.
 *
 * @param {parasprite.Type} t – one of schema root type or GraphQLObjectType
 *
 * @return {function}
 */
function setResolvers(t, obj) {
  for (const [name, resolver] of objectIterator.entries(obj)) {
    if (!(/\.__ignore__/.test(name) || resolver.ignore === true)) {
      setResolver(t, name, resolver)
    }
  }

  return t.end()
}

/**
 * Describe GraphQL schema using parasprite chainable API
 *
 * @return {graphql.GraphQLSchema}
 */
function makeSchema() {
  const resolvers = rd(SCHEMA_ROOT, {
    recurse: true
  })

  // Make parasprite.Schema instance to describe GraphQL schema for the app.
  let schema = Schema()

  invariant(
    isEmpty(resolvers.query), TypeError,
    "Query field is required for GraphQL schema."
  )

  schema = setResolvers(
    schema.query(
      "Shelves",
      "A Shelves type provides the root fields with available query resolvers."
    ),

    resolvers.query
  )

  // Add mutation resolvers if they exists
  if (!isEmpty(resolvers.mutation)) {
    schema = setResolvers(
      schema.mutation(
        "Spells",
        "A Spells type provides the root fields " +
        "with available mutation resolvers."
      ),

      resolvers.mutation
    )
  }

  // Add subscription resolvers if they exists
  if (!isEmpty(resolvers.subscription)) {
    schema = setResolvers(
      schema.subscription(
        "MagicFlow",
        "A MagicFlow type provides the root fields " +
        "with available subscription resolvers."
      ),

      resolvers.subscription
    )
  }

  return schema.end()
}

export default makeSchema()
<|endoftext|>


<commit_before>define([
    'preact',
    'htm',
    'uuid',

    'css!./IFrame.css'
], (
    preact,
    htm,
    {v4: uuidv4}
) => {

    const {h, Component, createRef} = preact;
    const html = htm.bind(h);

    class IFrame extends Component {
        constructor(props) {
            super(props);

            // const {
            //     origin, pathRoot, channelId, hostId,
            //     params, runtime
            // } = props;

            const id = uuidv4();
            this.id = `frame_ ${id}`;

            this.ref = createRef();

            const indexPath = [
                this.props.pathRoot,
                '/iframe_root/index.html',
                '#',
                this.props.original
            ].join('');

            // Make an absolute url to this.
            // TODO: add hash
            this.url = this.props.origin + '/' + indexPath; // + this.cacheBuster();

        }

        cacheBusterKey(buildInfo, developMode) {
            // NB developMode not implemented yet, so always defaults
            // to the gitCommitHash
            if (developMode) {
                return String(new Date().getTime());
            } else {
                return buildInfo.git.commitHash;
            }
        }

        cacheBuster() {
            // TODO: get develop mode from runtime
            return '?cb=' + this.cacheBusterKey(this.props.runtime.config('buildInfo'), false);
        }

        componentDidMount() {
            if (this.ref.current === null) {
                return;
            }
            this.props.whenMounted(this.ref.current.contentWindow);
        }

        render() {
            const params = {
                frameId: this.id,
                parentHost: document.location.origin,
                buildInfo: this.props.runtime.config('buildInfo'),
                developMode: false,
                params: this.props.params,
                channelId: this.props.hostChannelId,
                hostChannelId: this.props.hostChannelId,
                pluginChannelId: this.props.pluginChannelId
            };

            const paramString = window.encodeURIComponent(JSON.stringify(params));

            return html`
            <iframe id=${this.id}
                    name=${this.id}
                    data-k-b-testhook-iframe="plugin-iframe"
                    data-params=${paramString}
                    data-channel-id=${this.props.hostChannelId}
                    data-host-channel-id=${this.props.hostChannelId}
                    data-plugin-channel-id=${this.props.pluginChannelId}
                    className="IFrame -iframe"
                    frameborder="0"
                    scrolling="no"
                    ref=${this.ref}
                    src=${this.url}>
            `;
        }
    }

    return IFrame;
});
<commit_msg>restore cache busting to plugin url.<commit_after>define([
    'preact',
    'htm',
    'uuid',

    'css!./IFrame.css'
], (
    preact,
    htm,
    {v4: uuidv4}
) => {

    const {h, Component, createRef} = preact;
    const html = htm.bind(h);

    class IFrame extends Component {
        constructor(props) {
            super(props);

            // const {
            //     origin, pathRoot, channelId, hostId,
            //     params, runtime
            // } = props;

            const id = uuidv4();
            this.id = `frame_ ${id}`;

            this.ref = createRef();

            const indexPath = [
                this.props.pathRoot,
                '/iframe_root/index.html',
                '#',
                this.props.original
            ].join('');

            // Make an absolute url to this.
            // TODO: add hash
            this.url = this.props.origin + '/' + indexPath + this.cacheBuster();

        }

        cacheBusterKey(buildInfo, developMode) {
            // NB developMode not implemented yet, so always defaults
            // to the gitCommitHash
            if (developMode) {
                return String(new Date().getTime());
            } else {
                return buildInfo.git.commitHash;
            }
        }

        cacheBuster() {
            // TODO: get develop mode from runtime
            return '?__cb__=' + this.cacheBusterKey(this.props.runtime.config('buildInfo'), false);
        }

        componentDidMount() {
            if (this.ref.current === null) {
                return;
            }
            this.props.whenMounted(this.ref.current.contentWindow);
        }

        render() {
            const params = {
                frameId: this.id,
                parentHost: document.location.origin,
                buildInfo: this.props.runtime.config('buildInfo'),
                developMode: false,
                params: this.props.params,
                channelId: this.props.hostChannelId,
                hostChannelId: this.props.hostChannelId,
                pluginChannelId: this.props.pluginChannelId
            };

            const paramString = window.encodeURIComponent(JSON.stringify(params));

            return html`
            <iframe id=${this.id}
                    name=${this.id}
                    data-k-b-testhook-iframe="plugin-iframe"
                    data-params=${paramString}
                    data-channel-id=${this.props.hostChannelId}
                    data-host-channel-id=${this.props.hostChannelId}
                    data-plugin-channel-id=${this.props.pluginChannelId}
                    className="IFrame -iframe"
                    frameborder="0"
                    scrolling="no"
                    ref=${this.ref}
                    src=${this.url}>
            `;
        }
    }

    return IFrame;
});
<|endoftext|>


<commit_before>$(document).ready(function(){
    $("#domain").keyup(function(event){
        if(event.keyCode == 13){
            $("#submit").click();
        }
    });
});
window.onload = function() {
//Counts the number of requests in this session
    var requestNum = 0;
    //Choose the correct script to run based on dropdown selection
    document.getElementById("submit").onclick = function callRoute() {
            returnDnsDetails(document.getElementById("domain").value, document.getElementById("file").value)
    }

    function requestTitle(callType){
        switch(callType){
            case "getTxt.php":
                return "SPF/TXT Lookup";
                break;
            case "getMx.php":
                return "MX Lookup";
                break;
            case "getA.php":
                return "IP Lookup";
                break;
            case "getAll.php":
                return "All available DNS records";
                break;
            case "getAAAA.php":
                return "IPV6 Lookup";
                break;
            case "getWhois.php":
                return "Who Is Lookup";
                break;
            case "getHinfo.php":
                return "H Info Lookup";
                break;
        }
    }

    //Get DNS Details
    function returnDnsDetails(domain, callType) {
        //checks for valid input
        if (domain.length == 0) {
            document.getElementById("txtHint").innerHTML = " Please enter a valid domain";
            return;
        } else {
            var xmlhttp = new XMLHttpRequest();

            xmlhttp.onreadystatechange = function () {
                var date = new Date();
                if (this.readyState == 4 && this.status == 200) {
                    //Clears the hint field
                    document.getElementById("txtHint").innerHTML = "";
                    //parse the response into a JS Object
                    dnsResp = JSON.parse(this.responseText);

                    //cosole data validation
                    console.log(dnsResp);
                    console.log(dnsResp.length);

                    buildTable(dnsResp, callType);
                }
            };
            xmlhttp.onreadystatechange=function() {
                if (xmlhttp.readyState==4 && xmlhttp.status==200) {
                    document.getElementById("loading").innerHTML= '';
                }
            }
            document.getElementById("loading").innerHTML = '

'
            xmlhttp.open("GET", callType + "?domain=" + domain, true);
            xmlhttp.send();

        }
    }

    function buildTable(jsonResp, callType) {
        if (jsonResp.length == 0) {
            console.log("requestNum: " + requestNum);
            $(".responseTable").prepend("

");
            $(".responseRow" + requestNum + " Table").append("
" + requestTitle(callType) + "
");
            $(".responseRow" + requestNum + " Table").append("
NO DATA FOUND
");
        } else {

            //creates thes the table to store the response details each table has a unique class
            $(".responseTable").prepend("

");
            //Creates title bar
            $(".responseRow" + requestNum + " Table").append("
" + requestTitle(callType) + "
");

            for (i = 0, len = jsonResp.length; i < len; i++) {
                var jsonData = jsonResp[i];
                console.log(jsonData);

                if (i != 0) {$(".responseRow" + (requestNum-1)).append("

");}
                //iterates through object keys
                for (j = 0, len2 = Object.keys(jsonData).length; j < len2; j++) {
                    $(".responseRow" + requestNum + " Table").append("
" + Object.getOwnPropertyNames(jsonData)[j] + ":	" + jsonData[Object.keys(jsonData)[j]] + "
");
                }
                console.log("requestNum: " + requestNum);
                requestNum++;
                console.log("requestNum: " + requestNum);
            }

        }
    }
}<commit_msg>fix loading<commit_after>$(document).ready(function(){
    $("#domain").keyup(function(event){
        if(event.keyCode == 13){
            $("#submit").click();
        }
    });
});
window.onload = function() {
//Counts the number of requests in this session
    var requestNum = 0;
    //Choose the correct script to run based on dropdown selection
    document.getElementById("submit").onclick = function callRoute() {
            returnDnsDetails(document.getElementById("domain").value, document.getElementById("file").value)
    }

    function requestTitle(callType){
        switch(callType){
            case "getTxt.php":
                return "SPF/TXT Lookup";
                break;
            case "getMx.php":
                return "MX Lookup";
                break;
            case "getA.php":
                return "IP Lookup";
                break;
            case "getAll.php":
                return "All available DNS records";
                break;
            case "getAAAA.php":
                return "IPV6 Lookup";
                break;
            case "getWhois.php":
                return "Who Is Lookup";
                break;
            case "getHinfo.php":
                return "H Info Lookup";
                break;
        }
    }

    //Get DNS Details
    function returnDnsDetails(domain, callType) {
        //checks for valid input
        if (domain.length == 0) {
            document.getElementById("txtHint").innerHTML = " Please enter a valid domain";
            return;
        } else {
            var xmlhttp = new XMLHttpRequest();

            xmlhttp.onreadystatechange = function () {
                var date = new Date();
                if (this.readyState == 4 && this.status == 200) {
                    //Clears the hint field
                    document.getElementById("txtHint").innerHTML = "";
                    document.getElementById("loading").innerHTML= '';
                    //parse the response into a JS Object
                    dnsResp = JSON.parse(this.responseText);

                    //cosole data validation
                    console.log(dnsResp);
                    console.log(dnsResp.length);

                    buildTable(dnsResp, callType);
                }
            }
            document.getElementById("loading").innerHTML = '

'
            xmlhttp.open("GET", callType + "?domain=" + domain, true);
            xmlhttp.send();

        }
    }

    function buildTable(jsonResp, callType) {
        var requestNum = Date.now();
        if (jsonResp.length == 0) {
            console.log("requestNum: " + requestNum);
            $(".responseTable").prepend("

");
            $(".responseRow" + requestNum + " Table").append("
" + requestTitle(callType) + "
");
            $(".responseRow" + requestNum + " Table").append("
NO DATA FOUND
");
        } else {

            //creates thes the table to store the response details each table has a unique class
            $(".responseTable").prepend("

");
            //Creates title bar
            $(".responseRow" + requestNum + " Table").append("
" + requestTitle(callType) + "
");

            for (i = 0, len = jsonResp.length; i < len; i++) {
                var jsonData = jsonResp[i];
                console.log(jsonData);

                if (i != 0) {$(".responseRow" + (requestNum-1)).append("

");}
                //iterates through object keys
                for (j = 0, len2 = Object.keys(jsonData).length; j < len2; j++) {
                    $(".responseRow" + requestNum + " Table").append("
" + Object.getOwnPropertyNames(jsonData)[j] + ":	" + jsonData[Object.keys(jsonData)[j]] + "
");
                }
                console.log("requestNum: " + requestNum);
                requestNum++;
                console.log("requestNum: " + requestNum);
            }

        }
    }
}<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2018 The FOAM Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
  Accessible through browser at location path static/foam2/src/foam/nanos/controller/index.html
  Available on browser console as ctrl. (exports axiom)
*/
foam.CLASS({
  package: 'foam.nanos.controller',
  name: 'ApplicationController',
  extends: 'foam.u2.Element',

  documentation: 'FOAM Application Controller.',

  implements: [
    'foam.box.Context',
    'foam.nanos.controller.AppStyles'
  ],

  requires: [
    'foam.nanos.client.ClientBuilder',
    'foam.nanos.auth.Group',
    'foam.nanos.auth.ResendVerificationEmail',
    'foam.nanos.auth.SignInView',
    'foam.nanos.auth.User',
    'foam.nanos.auth.resetPassword.ResetView',
    'foam.nanos.u2.navigation.TopNavigation',
    'foam.nanos.u2.navigation.FooterView',
    'foam.u2.stack.Stack',
    'foam.u2.stack.StackView',
    'foam.u2.dialog.NotificationMessage',
    'foam.nanos.session.SessionTimer',
    'foam.u2.dialog.Popup'
  ],

  imports: [
    'installCSS',
    'sessionSuccess',
    'window'
  ],

  exports: [
    'appConfig',
    'as ctrl',
    'currentMenu',
    'group',
    'lastMenuLaunched',
    'lastMenuLaunchedListener',
    'loginSuccess',
    'lookAndFeel',
    'menuListener',
    'notify',
    'pushMenu',
    'requestLogin',
    'signUpEnabled',
    'stack',
    'user',
    'webApp',
    'wrapCSS as installCSS',
    'sessionTimer'
  ],

  constants: {
    MACROS: [
      'primaryColor',
      'secondaryColor',
      'tableColor',
      'tableHoverColor',
      'accentColor',
      'secondaryHoverColor',
      'secondaryDisabledColor',
      'destructiveColor',
      'destructiveHoverColor',
      'destructiveDisabledColor',
      'customCSS',
      'backgroundColor',
      'headerColor'
    ]
  },

  messages: [
    { name: 'GROUP_FETCH_ERR', message: 'Error fetching group' },
    { name: 'GROUP_NULL_ERR', message: 'Group was null' },
    { name: 'LOOK_AND_FEEL_NOT_FOUND', message: 'Could not fetch look and feel object.' }
  ],

  css: `
    body {
      font-family: 'Roboto', sans-serif;
      font-size: 14px;
      letter-spacing: 0.2px;
      color: #373a3c;
      background: %BACKGROUNDCOLOR%;
      margin: 0;
    }
    .stack-wrapper {
      margin-bottom: -10px;
      min-height: calc(80% - 60px);
    }
    .stack-wrapper:after {
      content: "";
      display: block;
    }
  `,

  properties: [
    {
      name: 'clientPromise',
      factory: function() {
        var self = this;
        return self.ClientBuilder.create().promise.then(function(cls) {
          self.client = cls.create(null, self);
          return self.client;
        });
      },
    },
    {
      name: 'client',
    },
    {
      name: 'appConfig',
      expression: function(client$appConfig) {
        return client$appConfig || null;
      }
    },
    {
      name: 'stack',
      factory: function() { return this.Stack.create(); }
    },
    {
      class: 'foam.core.FObjectProperty',
      of: 'foam.nanos.auth.User',
      name: 'user',
      factory: function() { return this.User.create(); }
    },
    {
      class: 'foam.core.FObjectProperty',
      of: 'foam.nanos.auth.Group',
      name: 'group'
    },
    {
      class: 'Boolean',
      name: 'signUpEnabled',
      adapt: function(_, v) {
        return foam.String.isInstance(v) ? v !== 'false' : v;
      }
    },
    {
      class: 'Boolean',
      name: 'loginSuccess'
    },
    {
      class: 'FObjectProperty',
      of: 'foam.nanos.session.SessionTimer',
      name: 'sessionTimer',
      factory: function() {
        return this.SessionTimer.create();
      }
    },
    {
      class: 'FObjectProperty',
      of: 'foam.nanos.auth.LookAndFeel',
      name: 'lookAndFeel'
    },

    // TODO: Get rid of all of the LookAndFeel-related properties on
    // ApplicationController and just read them off of the 'lookAndFeel'
    // property instead.
    'currentMenu',
    'lastMenuLaunched',
    'webApp',
    'topNavigation_',
    'footerView_'
  ],

  methods: [
    function init() {
      this.SUPER();

      // done to start using SectionedDetailViews instead of DetailViews
      foam.__context__.register(foam.u2.detail.SectionedDetailView, 'foam.u2.DetailView');

      var self = this;

      window.onpopstate = async function(event) {
        var hid = location.hash.substr(1);
        if ( hid ) {
          if ( self.client ) {
            var menu = await self.client.menuDAO.find(hid);
            menu && menu.launch(this);
          } else {
            self.clientPromise.then(async () => {
              var menu = await self.client.menuDAO.find(hid);
              menu && menu.launch(this);
            });
          }
        }
      };

      this.clientPromise.then(async function(client) {
        self.setPrivate_('__subContext__', client.__subContext__);

        await self.fetchAgent();
        await self.fetchUser();

        // Fetch the group only once the user has logged in. That's why we await
        // the line above before executing this one.
        await self.fetchGroup();
        self.onUserAgentAndGroupLoaded();
      });
    },

    function initE() {
      this.clientPromise.then(() => {
        this.fetchLookAndFeel().then(() => {
          this
            .addClass(this.myClass())
            .start('div', null, this.topNavigation_$)
              .tag(this.TopNavigation)
            .end()
            .start()
              .addClass('stack-wrapper')
              .tag({
                class: 'foam.u2.stack.StackView',
                data: this.stack,
                showActions: false
              })
            .end()
            .start('div', null, this.footerView_$)
              .tag(this.FooterView)
            .end();
          });
      });
    },

    async function fetchGroup() {
      try {
        var group = await this.client.auth.getCurrentGroup();
        if ( group == null ) throw new Error(this.GROUP_NULL_ERR);
        this.group = group;
      } catch (err) {
        this.notify(this.GROUP_FETCH_ERR, 'error');
        console.error(err.message || this.GROUP_FETCH_ERR);
      }
    },

    /**
     * Get current user, else show login.
     */
    async function fetchUser() {
      try {
        var result = await this.client.auth.getCurrentUser(null);
        this.loginSuccess = !! result;

        if ( ! result ) throw new Error();

        this.user = result;
      } catch (err) {
        await this.requestLogin();
        return await this.fetchUser();
      }
    },

    async function fetchAgent() {
      this.agent = await this.client.agentAuth.getCurrentAgent();
    },

    function expandShortFormMacro(css, m) {
      /* A short-form macros is of the form %PRIMARY_COLOR%. */
      var M = m.toUpperCase();

      return css.replace(
        new RegExp('%' + M + '%', 'g'),
        '/*%' + M + '%*/ ' + this.lookAndFeel[m]);
    },

    function expandLongFormMacro(css, m) {
      // A long-form macros is of the form "/*%PRIMARY_COLOR%*/ blue".
      var M = m.toUpperCase();

      return css.replace(
        new RegExp('/\\*%' + M + '%\\*/[^;]*', 'g'),
        '/*%' + M + '%*/ ' + this.lookAndFeel[m]);
    },

    // CSS preprocessor, works on classes instantiated in subContext
    function wrapCSS(text, id) {
      if ( text ) {
        var eid = foam.u2.Element.NEXT_ID();

        for ( var i = 0 ; i < this.MACROS.length ; i++ ) {
          let m     = this.MACROS[i];
          var text2 = this.expandShortFormMacro(this.expandLongFormMacro(text, m), m);

            // If the macro was found, then listen for changes to the property
            // and update the CSS if it changes.
            if ( text != text2 ) {
              text = text2;
              this.onDetach(this.lookAndFeel$.dot(m).sub(() => {
                var el = this.getElementById(eid);
                el.innerText = this.expandLongFormMacro(el.innerText, m);
              }));
            }
        }

        this.installCSS(text, id, eid);
      }
    },

    function pushMenu(menuId) {
      /** Use to load a specific menu. **/
      if ( window.location.hash.substr(1) != menuId ) {
        window.location.hash = menuId;
      }
    },

    function requestLogin() {
      var self = this;

      // don't go to log in screen if going to reset password screen
      if ( location.hash != null && location.hash === '#reset' ) {
        return new Promise(function(resolve, reject) {
          self.stack.push({ class: 'foam.nanos.auth.resetPassword.ResetView' });
          self.loginSuccess$.sub(resolve);
        });
      }

      return new Promise(function(resolve, reject) {
        self.stack.push({ class: 'foam.nanos.auth.SignInView' });
        self.loginSuccess$.sub(resolve);
      });
    },

    // This method is for toast notification message
    function notify(data, type) {
      this.add(this.NotificationMessage.create({
        message: data,
        type: type
      }));
    }
  ],

  listeners: [
    /**
     * Called whenever the group updates.
     *   - Updates the portal view based on the group
     *   - Update the look and feel of the app based on the group or user
     *   - Go to a menu based on either the hash or the group
     */
    function onUserAgentAndGroupLoaded() {
      if ( ! this.user.emailVerified ) {
        this.loginSuccess = false;
        this.stack.push({ class: 'foam.nanos.auth.ResendVerificationEmail' });
        return;
      }

      var hash = this.window.location.hash;
      if ( hash ) hash = hash.substring(1);

      if ( hash ) {
        window.onpopstate();
      } else if ( this.group ) {
        this.window.location.hash = this.group.defaultMenu;
      }

      // Update the look and feel now that the user is logged in since there
      // might be a more specific one to use now.
      this.fetchLookAndFeel();
    },

    // This listener should be called when a Menu item has been launched
    // by some Menu View. Is exported.
    function menuListener(m) {
      this.currentMenu = m;
    },

    // This listener should be called when a Menu has been launched but does
    // not navigate to a new screen. Typically for SubMenus
    function lastMenuLaunchedListener(m) {
      this.lastMenuLaunched = m;
    },

    // Get the most appropriate LookAndFeel object from the server and use it to
    // customize the look and feel of the application.
    async function fetchLookAndFeel() {
      try {
        this.lookAndFeel = await this.client.lookAndFeelService.getLookAndFeel(null, this.webApp);
      } catch (err) {
        this.notify(this.LOOK_AND_FEEL_NOT_FOUND, 'error');
        console.error(err);
        return;
      }

      this.useCustomElements();
    },

    // Use custom elements if supplied by the LookAndFeel.
    function useCustomElements() {
      if ( ! this.lookAndFeel ) throw new Error(this.LOOK_AND_FEEL_NOT_FOUND);

      if ( this.lookAndFeel.topNavigation && this.topNavigation_ ) {
        this.topNavigation_.removeAllChildren();
        this.topNavigation_.tag({ class: this.lookAndFeel.topNavigation });
      }

      if ( this.lookAndFeel.footerView && this.footerView_ ) {
        this.footerView_.removeAllChildren();
        this.footerView_.tag({ class: this.lookAndFeel.footerView });
      }
    }
  ]
});
<commit_msg>Remove resolved TODO<commit_after>/**
 * @license
 * Copyright 2018 The FOAM Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
  Accessible through browser at location path static/foam2/src/foam/nanos/controller/index.html
  Available on browser console as ctrl. (exports axiom)
*/
foam.CLASS({
  package: 'foam.nanos.controller',
  name: 'ApplicationController',
  extends: 'foam.u2.Element',

  documentation: 'FOAM Application Controller.',

  implements: [
    'foam.box.Context',
    'foam.nanos.controller.AppStyles'
  ],

  requires: [
    'foam.nanos.client.ClientBuilder',
    'foam.nanos.auth.Group',
    'foam.nanos.auth.ResendVerificationEmail',
    'foam.nanos.auth.SignInView',
    'foam.nanos.auth.User',
    'foam.nanos.auth.resetPassword.ResetView',
    'foam.nanos.u2.navigation.TopNavigation',
    'foam.nanos.u2.navigation.FooterView',
    'foam.u2.stack.Stack',
    'foam.u2.stack.StackView',
    'foam.u2.dialog.NotificationMessage',
    'foam.nanos.session.SessionTimer',
    'foam.u2.dialog.Popup'
  ],

  imports: [
    'installCSS',
    'sessionSuccess',
    'window'
  ],

  exports: [
    'appConfig',
    'as ctrl',
    'currentMenu',
    'group',
    'lastMenuLaunched',
    'lastMenuLaunchedListener',
    'loginSuccess',
    'lookAndFeel',
    'menuListener',
    'notify',
    'pushMenu',
    'requestLogin',
    'signUpEnabled',
    'stack',
    'user',
    'webApp',
    'wrapCSS as installCSS',
    'sessionTimer'
  ],

  constants: {
    MACROS: [
      'primaryColor',
      'secondaryColor',
      'tableColor',
      'tableHoverColor',
      'accentColor',
      'secondaryHoverColor',
      'secondaryDisabledColor',
      'destructiveColor',
      'destructiveHoverColor',
      'destructiveDisabledColor',
      'customCSS',
      'backgroundColor',
      'headerColor'
    ]
  },

  messages: [
    { name: 'GROUP_FETCH_ERR', message: 'Error fetching group' },
    { name: 'GROUP_NULL_ERR', message: 'Group was null' },
    { name: 'LOOK_AND_FEEL_NOT_FOUND', message: 'Could not fetch look and feel object.' }
  ],

  css: `
    body {
      font-family: 'Roboto', sans-serif;
      font-size: 14px;
      letter-spacing: 0.2px;
      color: #373a3c;
      background: %BACKGROUNDCOLOR%;
      margin: 0;
    }
    .stack-wrapper {
      margin-bottom: -10px;
      min-height: calc(80% - 60px);
    }
    .stack-wrapper:after {
      content: "";
      display: block;
    }
  `,

  properties: [
    {
      name: 'clientPromise',
      factory: function() {
        var self = this;
        return self.ClientBuilder.create().promise.then(function(cls) {
          self.client = cls.create(null, self);
          return self.client;
        });
      },
    },
    {
      name: 'client',
    },
    {
      name: 'appConfig',
      expression: function(client$appConfig) {
        return client$appConfig || null;
      }
    },
    {
      name: 'stack',
      factory: function() { return this.Stack.create(); }
    },
    {
      class: 'foam.core.FObjectProperty',
      of: 'foam.nanos.auth.User',
      name: 'user',
      factory: function() { return this.User.create(); }
    },
    {
      class: 'foam.core.FObjectProperty',
      of: 'foam.nanos.auth.Group',
      name: 'group'
    },
    {
      class: 'Boolean',
      name: 'signUpEnabled',
      adapt: function(_, v) {
        return foam.String.isInstance(v) ? v !== 'false' : v;
      }
    },
    {
      class: 'Boolean',
      name: 'loginSuccess'
    },
    {
      class: 'FObjectProperty',
      of: 'foam.nanos.session.SessionTimer',
      name: 'sessionTimer',
      factory: function() {
        return this.SessionTimer.create();
      }
    },
    {
      class: 'FObjectProperty',
      of: 'foam.nanos.auth.LookAndFeel',
      name: 'lookAndFeel'
    },
    'currentMenu',
    'lastMenuLaunched',
    'webApp',
    'topNavigation_',
    'footerView_'
  ],

  methods: [
    function init() {
      this.SUPER();

      // done to start using SectionedDetailViews instead of DetailViews
      foam.__context__.register(foam.u2.detail.SectionedDetailView, 'foam.u2.DetailView');

      var self = this;

      window.onpopstate = async function(event) {
        var hid = location.hash.substr(1);
        if ( hid ) {
          if ( self.client ) {
            var menu = await self.client.menuDAO.find(hid);
            menu && menu.launch(this);
          } else {
            self.clientPromise.then(async () => {
              var menu = await self.client.menuDAO.find(hid);
              menu && menu.launch(this);
            });
          }
        }
      };

      this.clientPromise.then(async function(client) {
        self.setPrivate_('__subContext__', client.__subContext__);

        await self.fetchAgent();
        await self.fetchUser();

        // Fetch the group only once the user has logged in. That's why we await
        // the line above before executing this one.
        await self.fetchGroup();
        self.onUserAgentAndGroupLoaded();
      });
    },

    function initE() {
      this.clientPromise.then(() => {
        this.fetchLookAndFeel().then(() => {
          this
            .addClass(this.myClass())
            .start('div', null, this.topNavigation_$)
              .tag(this.TopNavigation)
            .end()
            .start()
              .addClass('stack-wrapper')
              .tag({
                class: 'foam.u2.stack.StackView',
                data: this.stack,
                showActions: false
              })
            .end()
            .start('div', null, this.footerView_$)
              .tag(this.FooterView)
            .end();
          });
      });
    },

    async function fetchGroup() {
      try {
        var group = await this.client.auth.getCurrentGroup();
        if ( group == null ) throw new Error(this.GROUP_NULL_ERR);
        this.group = group;
      } catch (err) {
        this.notify(this.GROUP_FETCH_ERR, 'error');
        console.error(err.message || this.GROUP_FETCH_ERR);
      }
    },

    /**
     * Get current user, else show login.
     */
    async function fetchUser() {
      try {
        var result = await this.client.auth.getCurrentUser(null);
        this.loginSuccess = !! result;

        if ( ! result ) throw new Error();

        this.user = result;
      } catch (err) {
        await this.requestLogin();
        return await this.fetchUser();
      }
    },

    async function fetchAgent() {
      this.agent = await this.client.agentAuth.getCurrentAgent();
    },

    function expandShortFormMacro(css, m) {
      /* A short-form macros is of the form %PRIMARY_COLOR%. */
      var M = m.toUpperCase();

      return css.replace(
        new RegExp('%' + M + '%', 'g'),
        '/*%' + M + '%*/ ' + this.lookAndFeel[m]);
    },

    function expandLongFormMacro(css, m) {
      // A long-form macros is of the form "/*%PRIMARY_COLOR%*/ blue".
      var M = m.toUpperCase();

      return css.replace(
        new RegExp('/\\*%' + M + '%\\*/[^;]*', 'g'),
        '/*%' + M + '%*/ ' + this.lookAndFeel[m]);
    },

    // CSS preprocessor, works on classes instantiated in subContext
    function wrapCSS(text, id) {
      if ( text ) {
        var eid = foam.u2.Element.NEXT_ID();

        for ( var i = 0 ; i < this.MACROS.length ; i++ ) {
          let m     = this.MACROS[i];
          var text2 = this.expandShortFormMacro(this.expandLongFormMacro(text, m), m);

            // If the macro was found, then listen for changes to the property
            // and update the CSS if it changes.
            if ( text != text2 ) {
              text = text2;
              this.onDetach(this.lookAndFeel$.dot(m).sub(() => {
                var el = this.getElementById(eid);
                el.innerText = this.expandLongFormMacro(el.innerText, m);
              }));
            }
        }

        this.installCSS(text, id, eid);
      }
    },

    function pushMenu(menuId) {
      /** Use to load a specific menu. **/
      if ( window.location.hash.substr(1) != menuId ) {
        window.location.hash = menuId;
      }
    },

    function requestLogin() {
      var self = this;

      // don't go to log in screen if going to reset password screen
      if ( location.hash != null && location.hash === '#reset' ) {
        return new Promise(function(resolve, reject) {
          self.stack.push({ class: 'foam.nanos.auth.resetPassword.ResetView' });
          self.loginSuccess$.sub(resolve);
        });
      }

      return new Promise(function(resolve, reject) {
        self.stack.push({ class: 'foam.nanos.auth.SignInView' });
        self.loginSuccess$.sub(resolve);
      });
    },

    // This method is for toast notification message
    function notify(data, type) {
      this.add(this.NotificationMessage.create({
        message: data,
        type: type
      }));
    }
  ],

  listeners: [
    /**
     * Called whenever the group updates.
     *   - Updates the portal view based on the group
     *   - Update the look and feel of the app based on the group or user
     *   - Go to a menu based on either the hash or the group
     */
    function onUserAgentAndGroupLoaded() {
      if ( ! this.user.emailVerified ) {
        this.loginSuccess = false;
        this.stack.push({ class: 'foam.nanos.auth.ResendVerificationEmail' });
        return;
      }

      var hash = this.window.location.hash;
      if ( hash ) hash = hash.substring(1);

      if ( hash ) {
        window.onpopstate();
      } else if ( this.group ) {
        this.window.location.hash = this.group.defaultMenu;
      }

      // Update the look and feel now that the user is logged in since there
      // might be a more specific one to use now.
      this.fetchLookAndFeel();
    },

    // This listener should be called when a Menu item has been launched
    // by some Menu View. Is exported.
    function menuListener(m) {
      this.currentMenu = m;
    },

    // This listener should be called when a Menu has been launched but does
    // not navigate to a new screen. Typically for SubMenus
    function lastMenuLaunchedListener(m) {
      this.lastMenuLaunched = m;
    },

    // Get the most appropriate LookAndFeel object from the server and use it to
    // customize the look and feel of the application.
    async function fetchLookAndFeel() {
      try {
        this.lookAndFeel = await this.client.lookAndFeelService.getLookAndFeel(null, this.webApp);
      } catch (err) {
        this.notify(this.LOOK_AND_FEEL_NOT_FOUND, 'error');
        console.error(err);
        return;
      }

      this.useCustomElements();
    },

    // Use custom elements if supplied by the LookAndFeel.
    function useCustomElements() {
      if ( ! this.lookAndFeel ) throw new Error(this.LOOK_AND_FEEL_NOT_FOUND);

      if ( this.lookAndFeel.topNavigation && this.topNavigation_ ) {
        this.topNavigation_.removeAllChildren();
        this.topNavigation_.tag({ class: this.lookAndFeel.topNavigation });
      }

      if ( this.lookAndFeel.footerView && this.footerView_ ) {
        this.footerView_.removeAllChildren();
        this.footerView_.tag({ class: this.lookAndFeel.footerView });
      }
    }
  ]
});
<|endoftext|>