<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import SPELLS from 'common/SPELLS';

class HolyPriestSpreadsheet extends React.Component {
  static propTypes = {
    parser: PropTypes.object.isRequired,
  };

  render() {
    const { parser } = this.props;

    const styles = {
      cellBorder: { borderTop: '.5px solid #dddddd' },
      table: { borderBottom: '1px solid #dddddd', borderTop: '1px solid #dddddd', align: 'left', padding: '8px', float: 'left', margin: '2px' },
    };

    const getAbility = spellId => parser._modules.abilityTracker.getAbility(spellId);

    const overhealingSpell = spellId => ((getAbility(spellId).healingOverheal || 0) / ((getAbility(spellId).healingOverheal || 0) + (getAbility(spellId).healingEffective || 0)) || 0).toFixed(5);

    const cpm = spellId => (getAbility(spellId).casts / Math.floor(parser.fightDuration / 1000 / 60) || 0).toFixed(5);

    const targetsPerCast = (spellId, healId) => {
      const ability = getAbility(spellId);
      const heal = healId === undefined ? getAbility(spellId) : getAbility(healId);

      if (ability.ability == null) {
        return 'N/A';
      }

      let count = heal.healingHits / ability.casts;
      if (count) {
        return count.toFixed(5);
      }

      return JSON.stringify([ability, heal]);
    };

    const castEfficiency = (spellId) => {
      const efficiency = parser._modules.CastEfficiency.getCastEfficiencyForSpellId(spellId, true);
      if (!efficiency) {
        return 'N/A';
      }
      else if (efficiency.efficiency) {
        return efficiency.efficiency.toFixed(5);
      }
      else if (efficiency.maxCasts !== Infinity){
        return '0.00000';
      } else {
        return 1;
      }
    };

    return (

Please use the below table to populate the Player Log section of the Holy Priest Spreadsheet by Niphyr. Link to the sheet

                Intellect Rating
                {parser.selectedCombatant._combatantInfo.intellect}

                Critical Strike Rating
                {parser.selectedCombatant._combatantInfo.critSpell}

                Haste Rating
                {parser.selectedCombatant._combatantInfo.hasteSpell}

                Mastery Rating
                {parser.selectedCombatant._combatantInfo.mastery}

                Versatility Rating
                {parser.selectedCombatant._combatantInfo.versatilityHealingDone}

                Fight Length
                {Math.floor(parser.fightDuration / 1000)}

                Sanctify CDR (s)
                {parser._modules.serendipity.sanctify.rawReduction/1000}

                Serenity CDR (s)
                {parser._modules.serendipity.serenity.rawReduction/1000}

                Sanctify CDR wasted (s)
                {parser._modules.serendipity.sanctify.overcast/1000}

                Serenity CDR wasted (s)
                {parser._modules.serendipity.serenity.overcast/1000}

                Total Healing from Azerite Traits

                Renews refreshed by Enduring Renewal

                Renews from benediction

                Spell Name
                Overheal %
                CPM
                Casting Efficiency %
                Average Targets / Cast

                Heal
                {overhealingSpell(SPELLS.GREATER_HEAL.id)}
                {cpm(SPELLS.GREATER_HEAL.id)}
                {castEfficiency(SPELLS.GREATER_HEAL.id)}
                {targetsPerCast(SPELLS.GREATER_HEAL.id)}

                Flash Heal
                {overhealingSpell(SPELLS.FLASH_HEAL.id)}
                {cpm(SPELLS.FLASH_HEAL.id)}
                {castEfficiency(SPELLS.FLASH_HEAL.id)}
                {targetsPerCast(SPELLS.FLASH_HEAL.id)}

                Renew
                {overhealingSpell(SPELLS.RENEW.id)}
                {cpm(SPELLS.RENEW.id)}
                {castEfficiency(SPELLS.RENEW.id)}
                {targetsPerCast(SPELLS.RENEW.id)}

                Prayer of Healing
                {overhealingSpell(SPELLS.PRAYER_OF_HEALING.id)}
                {cpm(SPELLS.PRAYER_OF_HEALING.id)}
                {castEfficiency(SPELLS.PRAYER_OF_HEALING.id)}
                {targetsPerCast(SPELLS.PRAYER_OF_HEALING.id)}

                Prayer of Mending
                {overhealingSpell(SPELLS.PRAYER_OF_MENDING_HEAL.id)}
                {cpm(SPELLS.PRAYER_OF_MENDING_CAST.id)}
                {castEfficiency(SPELLS.PRAYER_OF_MENDING_CAST.id)}
                {targetsPerCast(SPELLS.PRAYER_OF_MENDING_CAST.id, SPELLS.PRAYER_OF_MENDING_HEAL.id)}

                Holy Word: Serenity
                {overhealingSpell(SPELLS.HOLY_WORD_SERENITY.id)}
                {cpm(SPELLS.HOLY_WORD_SERENITY.id)}
                {castEfficiency(SPELLS.HOLY_WORD_SERENITY.id)}
                {targetsPerCast(SPELLS.HOLY_WORD_SERENITY.id)}

                Holy Word: Sanctify
                {overhealingSpell(SPELLS.HOLY_WORD_SANCTIFY.id)}
                {(getAbility(SPELLS.HOLY_WORD_SANCTIFY.id).healingHits / Math.floor(parser.fightDuration / 1000 / 60) || 0).toFixed(5)}
                {castEfficiency(SPELLS.HOLY_WORD_SANCTIFY.id)}
                {targetsPerCast(SPELLS.HOLY_WORD_SANCTIFY.id)}

                Guardian Spirit
                {overhealingSpell(SPELLS.GUARDIAN_SPIRIT.id)}
                {cpm(SPELLS.GUARDIAN_SPIRIT.id)}
                {castEfficiency(SPELLS.GUARDIAN_SPIRIT.id)}
                1

                Divine Hymn
                {overhealingSpell(SPELLS.DIVINE_HYMN_HEAL.id)}
                {cpm(SPELLS.DIVINE_HYMN_CAST.id)}
                {castEfficiency(SPELLS.DIVINE_HYMN_CAST.id)}
                {targetsPerCast(SPELLS.DIVINE_HYMN_CAST.id, SPELLS.DIVINE_HYMN_HEAL.id)}

                Desperate Prayer
                {overhealingSpell(SPELLS.DESPERATE_PRAYER.id)}
                {cpm(SPELLS.DESPERATE_PRAYER.id)}
                {castEfficiency(SPELLS.DESPERATE_PRAYER.id)}
                1

                Cosmic Ripple
                {overhealingSpell(SPELLS.COSMIC_RIPPLE_HEAL.id)}
                N/A
                N/A
                N/A

                Binding Heal
                {overhealingSpell(SPELLS.BINDING_HEAL_TALENT.id)}
                {cpm(SPELLS.BINDING_HEAL_TALENT.id)}
                {castEfficiency(SPELLS.BINDING_HEAL_TALENT.id)}
                {targetsPerCast(SPELLS.BINDING_HEAL_TALENT.id)}

                Circle of Healing
                {overhealingSpell(SPELLS.CIRCLE_OF_HEALING_TALENT.id)}
                {cpm(SPELLS.CIRCLE_OF_HEALING_TALENT.id)}
                {castEfficiency(SPELLS.CIRCLE_OF_HEALING_TALENT.id)}
                {targetsPerCast(SPELLS.CIRCLE_OF_HEALING_TALENT.id)}

                Divine Star
                {overhealingSpell(SPELLS.DIVINE_STAR_TALENT.id)}
                {cpm(SPELLS.DIVINE_STAR_TALENT.id)}
                {castEfficiency(SPELLS.DIVINE_STAR_TALENT.id)}
                {targetsPerCast(SPELLS.DIVINE_STAR_TALENT.id)}

                Halo
                {overhealingSpell(SPELLS.HALO_TALENT.id)}
                {cpm(SPELLS.HALO_TALENT.id)}
                {castEfficiency(SPELLS.HALO_TALENT.id)}
                {targetsPerCast(SPELLS.HALO_TALENT.id)}

                Holy Word: Salvation
                {overhealingSpell(SPELLS.HOLY_WORD_SALVATION_TALENT.id)}
                {cpm(SPELLS.HOLY_WORD_SALVATION_TALENT.id)}
                {castEfficiency(SPELLS.HOLY_WORD_SALVATION_TALENT.id)}
                {targetsPerCast(SPELLS.HOLY_WORD_SALVATION_TALENT.id)}

    );
  }
}

export default HolyPriestSpreadsheet;
<commit_msg>adding bonus stats<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import SPELLS from 'common/SPELLS';

class HolyPriestSpreadsheet extends React.Component {
  static propTypes = {
    parser: PropTypes.object.isRequired,
  };

  render() {
    const { parser } = this.props;

    const styles = {
      cellBorder: { borderTop: '.5px solid #dddddd' },
      table: { borderBottom: '1px solid #dddddd', borderTop: '1px solid #dddddd', align: 'left', padding: '8px', float: 'left', margin: '2px' },
    };

    const getAbility = spellId => parser._modules.abilityTracker.getAbility(spellId);

    const overhealingSpell = spellId => ((getAbility(spellId).healingOverheal || 0) / ((getAbility(spellId).healingOverheal || 0) + (getAbility(spellId).healingEffective || 0)) || 0).toFixed(5);

    const cpm = spellId => (getAbility(spellId).casts / Math.floor(parser.fightDuration / 1000 / 60) || 0).toFixed(5);

    const targetsPerCast = (spellId, healId) => {
      const ability = getAbility(spellId);
      const heal = healId === undefined ? getAbility(spellId) : getAbility(healId);

      if (ability.ability == null) {
        return 'N/A';
      }

      let count = heal.healingHits / ability.casts;
      if (count) {
        return count.toFixed(5);
      }

      return JSON.stringify([ability, heal]);
    };

    const castEfficiency = (spellId) => {
      const efficiency = parser._modules.CastEfficiency.getCastEfficiencyForSpellId(spellId, true);
      if (!efficiency) {
        return 'N/A';
      }
      else if (efficiency.efficiency) {
        return efficiency.efficiency.toFixed(5);
      }
      else if (efficiency.maxCasts !== Infinity){
        return '0.00000';
      } else {
        return 1;
      }
    };

    return (

Please use the below table to populate the Player Log section of the Holy Priest Spreadsheet by Niphyr. Link to the sheet

                Intellect Rating
                {parser.selectedCombatant._combatantInfo.intellect}

                Critical Strike Rating
                {parser.selectedCombatant._combatantInfo.critSpell}

                Haste Rating
                {parser.selectedCombatant._combatantInfo.hasteSpell}

                Mastery Rating
                {parser.selectedCombatant._combatantInfo.mastery}

                Versatility Rating
                {parser.selectedCombatant._combatantInfo.versatilityHealingDone}

                Leech
                {parser.selectedCombatant._combatantInfo.leech}

                Avoidance
                {parser.selectedCombatant._combatantInfo.avoidance}

                Speed
                {parser.selectedCombatant._combatantInfo.speed}

                Fight Length
                {Math.floor(parser.fightDuration / 1000)}

                Sanctify CDR (s)
                {parser._modules.serendipity.sanctify.rawReduction/1000}

                Serenity CDR (s)
                {parser._modules.serendipity.serenity.rawReduction/1000}

                Sanctify CDR wasted (s)
                {parser._modules.serendipity.sanctify.overcast/1000}

                Serenity CDR wasted (s)
                {parser._modules.serendipity.serenity.overcast/1000}

                Total Healing from Azerite Traits

                Renews refreshed by Enduring Renewal

                Renews from benediction

                Spell Name
                Overheal %
                CPM
                Casting Efficiency %
                Average Targets / Cast

                Heal
                {overhealingSpell(SPELLS.GREATER_HEAL.id)}
                {cpm(SPELLS.GREATER_HEAL.id)}
                {castEfficiency(SPELLS.GREATER_HEAL.id)}
                {targetsPerCast(SPELLS.GREATER_HEAL.id)}

                Flash Heal
                {overhealingSpell(SPELLS.FLASH_HEAL.id)}
                {cpm(SPELLS.FLASH_HEAL.id)}
                {castEfficiency(SPELLS.FLASH_HEAL.id)}
                {targetsPerCast(SPELLS.FLASH_HEAL.id)}

                Renew
                {overhealingSpell(SPELLS.RENEW.id)}
                {cpm(SPELLS.RENEW.id)}
                {castEfficiency(SPELLS.RENEW.id)}
                {targetsPerCast(SPELLS.RENEW.id)}

                Prayer of Healing
                {overhealingSpell(SPELLS.PRAYER_OF_HEALING.id)}
                {cpm(SPELLS.PRAYER_OF_HEALING.id)}
                {castEfficiency(SPELLS.PRAYER_OF_HEALING.id)}
                {targetsPerCast(SPELLS.PRAYER_OF_HEALING.id)}

                Prayer of Mending
                {overhealingSpell(SPELLS.PRAYER_OF_MENDING_HEAL.id)}
                {cpm(SPELLS.PRAYER_OF_MENDING_CAST.id)}
                {castEfficiency(SPELLS.PRAYER_OF_MENDING_CAST.id)}
                {targetsPerCast(SPELLS.PRAYER_OF_MENDING_CAST.id, SPELLS.PRAYER_OF_MENDING_HEAL.id)}

                Holy Word: Serenity
                {overhealingSpell(SPELLS.HOLY_WORD_SERENITY.id)}
                {cpm(SPELLS.HOLY_WORD_SERENITY.id)}
                {castEfficiency(SPELLS.HOLY_WORD_SERENITY.id)}
                {targetsPerCast(SPELLS.HOLY_WORD_SERENITY.id)}

                Holy Word: Sanctify
                {overhealingSpell(SPELLS.HOLY_WORD_SANCTIFY.id)}
                {(getAbility(SPELLS.HOLY_WORD_SANCTIFY.id).healingHits / Math.floor(parser.fightDuration / 1000 / 60) || 0).toFixed(5)}
                {castEfficiency(SPELLS.HOLY_WORD_SANCTIFY.id)}
                {targetsPerCast(SPELLS.HOLY_WORD_SANCTIFY.id)}

                Guardian Spirit
                {overhealingSpell(SPELLS.GUARDIAN_SPIRIT.id)}
                {cpm(SPELLS.GUARDIAN_SPIRIT.id)}
                {castEfficiency(SPELLS.GUARDIAN_SPIRIT.id)}
                1

                Divine Hymn
                {overhealingSpell(SPELLS.DIVINE_HYMN_HEAL.id)}
                {cpm(SPELLS.DIVINE_HYMN_CAST.id)}
                {castEfficiency(SPELLS.DIVINE_HYMN_CAST.id)}
                {targetsPerCast(SPELLS.DIVINE_HYMN_CAST.id, SPELLS.DIVINE_HYMN_HEAL.id)}

                Desperate Prayer
                {overhealingSpell(SPELLS.DESPERATE_PRAYER.id)}
                {cpm(SPELLS.DESPERATE_PRAYER.id)}
                {castEfficiency(SPELLS.DESPERATE_PRAYER.id)}
                1

                Cosmic Ripple
                {overhealingSpell(SPELLS.COSMIC_RIPPLE_HEAL.id)}
                N/A
                N/A
                N/A

                Binding Heal
                {overhealingSpell(SPELLS.BINDING_HEAL_TALENT.id)}
                {cpm(SPELLS.BINDING_HEAL_TALENT.id)}
                {castEfficiency(SPELLS.BINDING_HEAL_TALENT.id)}
                {targetsPerCast(SPELLS.BINDING_HEAL_TALENT.id)}

                Circle of Healing
                {overhealingSpell(SPELLS.CIRCLE_OF_HEALING_TALENT.id)}
                {cpm(SPELLS.CIRCLE_OF_HEALING_TALENT.id)}
                {castEfficiency(SPELLS.CIRCLE_OF_HEALING_TALENT.id)}
                {targetsPerCast(SPELLS.CIRCLE_OF_HEALING_TALENT.id)}

                Divine Star
                {overhealingSpell(SPELLS.DIVINE_STAR_TALENT.id)}
                {cpm(SPELLS.DIVINE_STAR_TALENT.id)}
                {castEfficiency(SPELLS.DIVINE_STAR_TALENT.id)}
                {targetsPerCast(SPELLS.DIVINE_STAR_TALENT.id)}

                Halo
                {overhealingSpell(SPELLS.HALO_TALENT.id)}
                {cpm(SPELLS.HALO_TALENT.id)}
                {castEfficiency(SPELLS.HALO_TALENT.id)}
                {targetsPerCast(SPELLS.HALO_TALENT.id)}

                Holy Word: Salvation
                {overhealingSpell(SPELLS.HOLY_WORD_SALVATION_TALENT.id)}
                {cpm(SPELLS.HOLY_WORD_SALVATION_TALENT.id)}
                {castEfficiency(SPELLS.HOLY_WORD_SALVATION_TALENT.id)}
                {targetsPerCast(SPELLS.HOLY_WORD_SALVATION_TALENT.id)}

    );
  }
}

export default HolyPriestSpreadsheet;
<|endoftext|>


<commit_before>'use strict';

/* http://docs.angularjs.org/guide/dev_guide.e2e-testing */

describe('my app', function() {

  beforeEach(function() {
    browser().navigateTo('app/index.html');
  });

});

    it('should render phone specific links', function() {
      input('query').enter('nexus');
      element('.phones li a').click();
      expect(browser().location().url()).toBe('/phones/nexus-s');
    });

<commit_msg>fixed e2e test<commit_after>'use strict';

/* e2e-testing */

describe('PhoneCat App', function() {

  describe('Phone list view', function() {

    beforeEach(function() {
      browser().navigateTo('app/index.html');
    });

    it('should filter the phone list as user types into the search box', function() {
      expect(repeater('.phones li').count()).toBe(20);

      input('phoneQuery').enter('nexus');
      expect(repeater('.phones li').count()).toBe(1);

      input('phoneQuery').enter('motorola');
      expect(repeater('.phones li').count()).toBe(8);
    });

    it('should be possible to control phone order via the drop down select box', function() {
      input('phoneQuery').enter('tablet'); //let's narrow the dataset to make the test assertions shorter

      expect(repeater('.phones li', 'Phone List').column('phone.name')).
          toEqual(["Motorola XOOM\u2122 with Wi-Fi",
                   "MOTOROLA XOOM\u2122"]);

      select('phonesOrder').option('Alphabetical');

      expect(repeater('.phones li', 'Phone List').column('phone.name')).
          toEqual(["MOTOROLA XOOM\u2122",
                   "Motorola XOOM\u2122 with Wi-Fi"]);
    });

    it('should render phone specific links', function() {
      input('phoneQuery').enter('nexus');
      element('.phones li a').click();
      expect(browser().location().url()).toBe('/phones/nexus-s');
    });
  });
});<|endoftext|>


<commit_before>/*global define*/
define(['../Core/createGuid',
        '../Core/Cartographic',
        '../Core/ClockRange',
        '../Core/ClockStep',
        '../Core/DeveloperError',
        '../Core/RuntimeError',
        '../Core/Ellipsoid',
        '../Core/Event',
        '../Core/Iso8601',
        '../Core/loadXML',
        './DynamicClock',
        './DynamicObjectCollection',
        './DynamicPoint',
        './DynamicPolyline',
        './DynamicPolygon',
        './DynamicLabel',
        './DynamicBillboard'
        ], function(
                createGuid,
                Cartographic,
                ClockRange,
                ClockStep,
                DeveloperError,
                RuntimeError,
                Ellipsoid,
                Event,
                Iso8601,
                loadXML,
                DynamicClock,
                DynamicObjectCollection,
                DynamicPoint,
                DynamicPolyline,
                DynamicPolygon,
                DynamicLabel,
                DynamicBillboard) {
    "use strict";

    //Copied from GeoJsonDataSource
    var ConstantPositionProperty = function(value) {
        this._value = value;
    };

    ConstantPositionProperty.prototype.getValueCartesian = function(time, result) {
        var value = this._value;
        if (typeof value.clone === 'function') {
            return value.clone(result);
        }
        return value;
    };

    ConstantPositionProperty.prototype.setValue = function(value) {
        this._value = value;
    };

    function createObject(kml, dynamicObjectCollection) {
        var id = kml.id;
        if (typeof id === 'undefined') {
            id = createGuid();
        } else {
            var i = 2;
            var finalId = id;
            while (typeof dynamicObjectCollection.getObject(finalId) !== 'undefined') {
                finalId = id + "_" + i;
                i++;
            }
            id = finalId;
        }
        var dynamicObject = dynamicObjectCollection.getOrCreateObject(id);
        dynamicObject.kml = kml;
        return dynamicObject;
    }

    function readCoordinates(el) {
        var text = "", coords = [], i;
        for (i = 0; i < el.childNodes.length; i++) {
            text = text + el.childNodes[i].nodeValue;
        }
        text = text.split(/[\s\n]+/);
        for (i = 0; i < text.length; i++) {
            var ll = text[i].split(',');
            if (ll.length < 2) {
                continue;
            }
            coords.push(ll[0]);
            coords.push(ll[1]);
            if(typeof ll[2] === 'undefined'){
                coords.push(ll[2]); //defaultValue will handle it
            } else {
                coords.push(parseInt(ll[2], 10));
            }
        }
        return coords;
    }

    // KML processing functions
    function processPlacemark(dataSource, placemark, dynamicObjectCollection) {
        //TODO get placemark ID in the proper way
        var objectId = placemark.id;
        if (typeof objectId === 'undefined') {
            objectId = createGuid();
        }
        dynamicObjectCollection.getOrCreateObject(objectId); //dataSource._dynamicObjectCollection...?

        // I want to iterate over every placemark
        for(var i = 0, len = placemark.childNodes.length; i < len; i++){
            var node = placemark.childNodes.item(i);
            //Checking if the node holds a supported Geometry type
            if(geometryTypes.hasOwnProperty(node.nodeName)){
                placemark.geometry = node.nodeName;
                var geometryType = placemark.geometry;
                var geometryHandler = geometryTypes[geometryType];
                if (typeof geometryHandler === 'undefined') {
                    throw new RuntimeError('Unknown geometry type: ' + geometryType);
                }
                geometryHandler(dataSource, placemark, node);
            }
        }
    }

    function processPoint(dataSource, kml, node) {
        var el = node.getElementsByTagName('coordinates');
        var coordinates = [];
        for (var j = 0; j < el.length; j++) {
            coordinates = coordinates.concat(readCoordinates(el[j]));
        }

        var cartographic = Cartographic.fromDegrees(coordinates[0], coordinates[1], coordinates[2]);
        var cartesian3 = Ellipsoid.WGS84.cartographicToCartesian(cartographic);
        var dynamicObject = createObject(node, dataSource._dynamicObjectCollection);
        //dynamicObject.merge(dataSource.defaultPoint);  What are the defaults for KML?
        dynamicObject.position = new ConstantPositionProperty(cartesian3);

        //add the new dynamicObject to dataSource._dynamicObjectCollection?
    }

    function processLineString(dataSource, kml, node){

    }

    function processLinearRing(dataSource, kml, node){

    }

    function processPolygon(dataSource, kml, node){

    }

    function processMultiGeometry(dataSource, kml, node){

    }

    function processModel(dataSource, kml, node){

    }

    function processGxTrack(dataSource, kml, node){

    }

    function processGxMultiTrack(dataSource, kml, node){

    }

    //Object that holds all supported Geometry
    var geometryTypes = {
            Point : processPoint,
            LineString : processLineString,
            LinearRing : processLinearRing,
            Polygon : processPolygon,
            MultiGeometry : processMultiGeometry,
            Model : processModel,
            gxTrack : processGxTrack,
            gxMultitrack : processGxMultiTrack
        };

    function processStyle(styleNode, dynamicObject) {
        for(var i = 0, len = styleNode.childNodes.length; i < len; i++){
            var node = styleNode.childNodes.item(i);

            if(node.nodeName === "IconStyle"){
                dynamicObject.billboard = new DynamicBillboard();
                //Map style to billboard properties
            }
            if(node.nodeName ===  "LabelStyle")   {
                dynamicObject.label = new DynamicLabel();
                //Map style to label properties
            }
            if(node.nodeName ===  "LineStyle")   {
                dynamicObject.polyline = new DynamicPolyline();
                //Map style to line properties
            }
            if(node.nodeName === "PolyStyle")   {
                dynamicObject.polygon = new DynamicPolygon();
                //Map style to polygon properties
            }
        }
    }

    function processFolder(){

    }

    function loadKML(dataSource, kml, sourceUri) {
        var dynamicObjectCollection = dataSource._dynamicObjectCollection;

        if (typeof kml === 'undefined') {
            throw new DeveloperError('kml is required.');
        }
        if (typeof dynamicObjectCollection === 'undefined') {
            throw new DeveloperError('dynamicObjectCollection is required.');
        }

        var stylesArray = kml.getElementsByTagName('Style');
        var styleCollection = new DynamicObjectCollection();
        for ( var i = 0, len = stylesArray.length; i < len; i++){
            var styleNode = stylesArray.item(i);
            styleNode.id = stylesArray.item(i).attributes[0].nodeValue;
            var styleObject = styleCollection.getOrCreateObject(styleNode.id);

            processStyle(styleNode, styleObject);
        }

        var array = kml.getElementsByTagName('Folder');
        for (i = 0, len = array.length; i < len; i++){
            processFolder(dataSource, array[i], dynamicObjectCollection);
        }

        array = kml.getElementsByTagName('Placemark');
//      //Then, when iterating placemarks you do something like this
//
//      If(placemark node has embedded style) {
//
//          //process the style directly
//
//          processStyle(styleNode, placemarkDynamicObject);
//
//      } else {
//
//          //Shared style uri, so get the already processed style and merge it with this object.
//
//          var styleObject = styleCollection.getObject(style Uri);
//
//          placemarkDynamicObject.merge(styleObject);
//      }
        for (i = 0, len = array.length; i < len; i++){
            processPlacemark(dataSource, array[i], dynamicObjectCollection);
        }
    }

    /**
     * A {@link DataSource} which processes KML.
     * @alias KmlDataSource
     * @constructor
     */
    var KmlDataSource = function(){
        this._changed = new Event();
        this._error = new Event();
        this._clock = undefined;
        this._dynamicObjectCollection = new DynamicObjectCollection();
        this._timeVarying = true;
    };

    /**
     * Gets an event that will be raised when non-time-varying data changes
     * or if the return value of getIsTimeVarying changes.
     * @memberof DataSource
     *
     * @returns {Event} The event.
     */
    KmlDataSource.prototype.getChangedEvent = function() {
        return this._changed;
    };

    /**
     * Gets an event that will be raised if an error is encountered during processing.
     * @memberof KmlDataSource
     *
     * @returns {Event} The event.
     */
    KmlDataSource.prototype.getErrorEvent = function() {
        return this._error;
    };

    /**
     * Gets the top level clock defined in KML or the availability of the
     * underlying data if no clock is defined.  If the KML document only contains
     * infinite data, undefined will be returned.
     * @memberof KmlDataSource
     *
     * @returns {DynamicClock} The clock associated with the current KML data, or undefined if none exists.
     */
    KmlDataSource.prototype.getClock = function() {
        return undefined;
    };

    /**
     * Gets the DynamicObjectCollection generated by this data source.
     * @memberof DataSource
     *
     * @returns {DynamicObjectCollection} The collection of objects generated by this data source.
     */
    KmlDataSource.prototype.getDynamicObjectCollection = function() {
        return this._dynamicObjectCollection;
    };

    /**
     * Gets a value indicating if the data varies with simulation time.  If the return value of
     * this function changes, the changed event will be raised.
     * @memberof DataSource
     *
     * @returns {Boolean} True if the data is varies with simulation time, false otherwise.
     */
    KmlDataSource.prototype.getIsTimeVarying = function() {
        return false;
    };

    /**
     * Replaces any existing data with the provided KML.
     *
     * @param {Object} KML The KML to be processed.
     * @param {String} source The source of the KML.
     *
     * @exception {DeveloperError} KML is required.
     */
    KmlDataSource.prototype.load = function(kml, source) {
        if (typeof kml === 'undefined') {
            throw new DeveloperError('kml is required.');
        }

        this._dynamicObjectCollection.clear();
        loadKML(this, kml, source);
    };

    /**
     * Asynchronously loads the KML at the provided url, replacing any existing data.
     *
     * @param {Object} url The url to be processed.
     *
     * @returns {Promise} a promise that will resolve when the KML is processed.
     *
     * @exception {DeveloperError} url is required.
     */
    KmlDataSource.prototype.loadUrl = function(url) {
        if (typeof url === 'undefined') {
            throw new DeveloperError('url is required.');
        }

        var dataSource = this;
        return loadXML(url).then(function(kml) {
            dataSource.load(kml, url);
        }, function(error) {
            this._error.raiseEvent(this, error);
        });
    };

    return KmlDataSource;
});<commit_msg>Fix how IDs are discovered or created<commit_after>/*global define*/
define(['../Core/createGuid',
        '../Core/Cartographic',
        '../Core/ClockRange',
        '../Core/ClockStep',
        '../Core/DeveloperError',
        '../Core/RuntimeError',
        '../Core/Ellipsoid',
        '../Core/Event',
        '../Core/Iso8601',
        '../Core/loadXML',
        './DynamicClock',
        './DynamicObjectCollection',
        './DynamicPoint',
        './DynamicPolyline',
        './DynamicPolygon',
        './DynamicLabel',
        './DynamicBillboard'
        ], function(
                createGuid,
                Cartographic,
                ClockRange,
                ClockStep,
                DeveloperError,
                RuntimeError,
                Ellipsoid,
                Event,
                Iso8601,
                loadXML,
                DynamicClock,
                DynamicObjectCollection,
                DynamicPoint,
                DynamicPolyline,
                DynamicPolygon,
                DynamicLabel,
                DynamicBillboard) {
    "use strict";

    //Copied from GeoJsonDataSource
    var ConstantPositionProperty = function(value) {
        this._value = value;
    };

    ConstantPositionProperty.prototype.getValueCartesian = function(time, result) {
        var value = this._value;
        if (typeof value.clone === 'function') {
            return value.clone(result);
        }
        return value;
    };

    ConstantPositionProperty.prototype.setValue = function(value) {
        this._value = value;
    };

    function createObject(kml, dynamicObjectCollection) {
        var id = kml.id;
        if (typeof id === 'undefined') {
            id = createGuid();
        } else {
            var i = 2;
            var finalId = id;
            while (typeof dynamicObjectCollection.getObject(finalId) !== 'undefined') {
                finalId = id + "_" + i;
                i++;
            }
            id = finalId;
        }
        var dynamicObject = dynamicObjectCollection.getOrCreateObject(id);
        dynamicObject.kml = kml;
        return dynamicObject;
    }

    //Helper functions
    function readCoordinates(el) {
        var text = "", coords = [], i;
        for (i = 0; i < el.childNodes.length; i++) {
            text = text + el.childNodes[i].nodeValue;
        }
        text = text.split(/[\s\n]+/);
        for (i = 0; i < text.length; i++) {
            var ll = text[i].split(',');
            if (ll.length < 2) {
                continue;
            }
            coords.push(ll[0]);
            coords.push(ll[1]);
            if(typeof ll[2] === 'undefined'){
                coords.push(ll[2]); //defaultValue will handle it
            } else {
                coords.push(parseInt(ll[2], 10));
            }
        }
        return coords;
    }

    function getId(node){
        var id = node.attributes.id && node.attributes.id.nodeValue;
        if (typeof id === 'undefined') {
            id = createGuid();
        }
        return id;
    }

    // KML processing functions
    function processPlacemark(dataSource, placemark, dynamicObjectCollection) {
        placemark.id = getId(placemark);
        dynamicObjectCollection.getOrCreateObject(placemark.id); //dataSource._dynamicObjectCollection...?

        // I want to iterate over every placemark
        for(var i = 0, len = placemark.childNodes.length; i < len; i++){
            var node = placemark.childNodes.item(i);
            //Checking if the node holds a supported Geometry type
            if(geometryTypes.hasOwnProperty(node.nodeName)){
                placemark.geometry = node.nodeName;
                var geometryType = placemark.geometry;
                var geometryHandler = geometryTypes[geometryType];
                if (typeof geometryHandler === 'undefined') {
                    throw new RuntimeError('Unknown geometry type: ' + geometryType);
                }
                geometryHandler(dataSource, placemark, node);
            }
        }
    }

    function processPoint(dataSource, kml, node) {
        var el = node.getElementsByTagName('coordinates');
        var coordinates = [];
        for (var j = 0; j < el.length; j++) {
            coordinates = coordinates.concat(readCoordinates(el[j]));
        }

        var cartographic = Cartographic.fromDegrees(coordinates[0], coordinates[1], coordinates[2]);
        var cartesian3 = Ellipsoid.WGS84.cartographicToCartesian(cartographic);
        var dynamicObject = createObject(node, dataSource._dynamicObjectCollection);
        //dynamicObject.merge(dataSource.defaultPoint);  What are the defaults for KML?
        dynamicObject.position = new ConstantPositionProperty(cartesian3);

        //add the new dynamicObject to dataSource._dynamicObjectCollection?
    }

    function processLineString(dataSource, kml, node){

    }

    function processLinearRing(dataSource, kml, node){

    }

    function processPolygon(dataSource, kml, node){

    }

    function processMultiGeometry(dataSource, kml, node){

    }

    function processModel(dataSource, kml, node){

    }

    function processGxTrack(dataSource, kml, node){

    }

    function processGxMultiTrack(dataSource, kml, node){

    }

    //Object that holds all supported Geometry
    var geometryTypes = {
            Point : processPoint,
            LineString : processLineString,
            LinearRing : processLinearRing,
            Polygon : processPolygon,
            MultiGeometry : processMultiGeometry,
            Model : processModel,
            gxTrack : processGxTrack,
            gxMultitrack : processGxMultiTrack
        };

    function processStyle(styleNode, dynamicObject) {
        for(var i = 0, len = styleNode.childNodes.length; i < len; i++){
            var node = styleNode.childNodes.item(i);

            if(node.nodeName === "IconStyle"){
                dynamicObject.billboard = new DynamicBillboard();
                //Map style to billboard properties
            }
            if(node.nodeName ===  "LabelStyle")   {
                dynamicObject.label = new DynamicLabel();
                //Map style to label properties
            }
            if(node.nodeName ===  "LineStyle")   {
                dynamicObject.polyline = new DynamicPolyline();
                //Map style to line properties
            }
            if(node.nodeName === "PolyStyle")   {
                dynamicObject.polygon = new DynamicPolygon();
                //Map style to polygon properties
            }
        }
    }

    function processFolder(){

    }

    function loadKML(dataSource, kml, sourceUri) {
        var dynamicObjectCollection = dataSource._dynamicObjectCollection;

        if (typeof kml === 'undefined') {
            throw new DeveloperError('kml is required.');
        }
        if (typeof dynamicObjectCollection === 'undefined') {
            throw new DeveloperError('dynamicObjectCollection is required.');
        }

        var stylesArray = kml.getElementsByTagName('Style');
        var styleCollection = new DynamicObjectCollection();
        for ( var i = 0, len = stylesArray.length; i < len; i++){
            var styleNode = stylesArray.item(i);
            styleNode.id = getId(styleNode);
            var styleObject = styleCollection.getOrCreateObject(styleNode.id);

            processStyle(styleNode, styleObject);
        }

        var array = kml.getElementsByTagName('Folder');
        for (i = 0, len = array.length; i < len; i++){
            processFolder(dataSource, array[i], dynamicObjectCollection);
        }

        array = kml.getElementsByTagName('Placemark');
//      //Then, when iterating placemarks you do something like this
//
//      If(placemark node has embedded style) {
//
//          //process the style directly
//
//          processStyle(styleNode, placemarkDynamicObject);
//
//      } else {
//
//          //Shared style uri, so get the already processed style and merge it with this object.
//
//          var styleObject = styleCollection.getObject(style Uri);
//
//          placemarkDynamicObject.merge(styleObject);
//      }
        for (i = 0, len = array.length; i < len; i++){
            processPlacemark(dataSource, array[i], dynamicObjectCollection);
        }
    }

    /**
     * A {@link DataSource} which processes KML.
     * @alias KmlDataSource
     * @constructor
     */
    var KmlDataSource = function(){
        this._changed = new Event();
        this._error = new Event();
        this._clock = undefined;
        this._dynamicObjectCollection = new DynamicObjectCollection();
        this._timeVarying = true;
    };

    /**
     * Gets an event that will be raised when non-time-varying data changes
     * or if the return value of getIsTimeVarying changes.
     * @memberof DataSource
     *
     * @returns {Event} The event.
     */
    KmlDataSource.prototype.getChangedEvent = function() {
        return this._changed;
    };

    /**
     * Gets an event that will be raised if an error is encountered during processing.
     * @memberof KmlDataSource
     *
     * @returns {Event} The event.
     */
    KmlDataSource.prototype.getErrorEvent = function() {
        return this._error;
    };

    /**
     * Gets the top level clock defined in KML or the availability of the
     * underlying data if no clock is defined.  If the KML document only contains
     * infinite data, undefined will be returned.
     * @memberof KmlDataSource
     *
     * @returns {DynamicClock} The clock associated with the current KML data, or undefined if none exists.
     */
    KmlDataSource.prototype.getClock = function() {
        return undefined;
    };

    /**
     * Gets the DynamicObjectCollection generated by this data source.
     * @memberof DataSource
     *
     * @returns {DynamicObjectCollection} The collection of objects generated by this data source.
     */
    KmlDataSource.prototype.getDynamicObjectCollection = function() {
        return this._dynamicObjectCollection;
    };

    /**
     * Gets a value indicating if the data varies with simulation time.  If the return value of
     * this function changes, the changed event will be raised.
     * @memberof DataSource
     *
     * @returns {Boolean} True if the data is varies with simulation time, false otherwise.
     */
    KmlDataSource.prototype.getIsTimeVarying = function() {
        return false;
    };

    /**
     * Replaces any existing data with the provided KML.
     *
     * @param {Object} KML The KML to be processed.
     * @param {String} source The source of the KML.
     *
     * @exception {DeveloperError} KML is required.
     */
    KmlDataSource.prototype.load = function(kml, source) {
        if (typeof kml === 'undefined') {
            throw new DeveloperError('kml is required.');
        }

        this._dynamicObjectCollection.clear();
        loadKML(this, kml, source);
    };

    /**
     * Asynchronously loads the KML at the provided url, replacing any existing data.
     *
     * @param {Object} url The url to be processed.
     *
     * @returns {Promise} a promise that will resolve when the KML is processed.
     *
     * @exception {DeveloperError} url is required.
     */
    KmlDataSource.prototype.loadUrl = function(url) {
        if (typeof url === 'undefined') {
            throw new DeveloperError('url is required.');
        }

        var dataSource = this;
        return loadXML(url).then(function(kml) {
            dataSource.load(kml, url);
        }, function(error) {
            this._error.raiseEvent(this, error);
        });
    };

    return KmlDataSource;
});<|endoftext|>


<commit_before>/* global describe, beforeEach, it, expect, browser, element, input, select */
'use strict';

// http://docs.angularjs.org/guide/dev_guide.e2e-testing
// This scenario has to be run while monkey-tail is running with
// the fixtures in test/fixtures.
describe('my app', function() {
    beforeEach(function() {
        browser().navigateTo('/app/index.html');

        // TODO: not so great to logout before every test
        element('button.navLogout').click();
    });

    it('should redirect to /login when location hash is empty', function() {
        expect(browser().location().url()).toBe('/login');
    });

    it('should redirect to /login for pages needing authentication', function() {
        browser().navigateTo('#/socialGraph');
        expect(browser().location().url()).toBe('/login');

        browser().navigateTo('#/activity');
        expect(browser().location().url()).toBe('/login');
    });

    describe('login', function() {
        beforeEach(function() {
            browser().navigateTo('#/login');
        });

        it('should render login form when navigating to /login', function() {
            expect(element('[ng-view] form').count()).toBe(1);
            expect(element('[ng-view] form input[type=password]').count()).toBe(1);
        });

        describe('authenticated user', function() {
            beforeEach(function() {
                input('form.email').enter('foo@bar.baz');
                input('form.password').enter('foobar');
                element('[ng-view] form button').click();
            });

            it('should be possible to login with correct username and pw', function() {
                expect(browser().location().url()).toBe('/socialGraph');
            });

            describe('socialGraph', function() {
                beforeEach(function() {
                    browser().navigateTo('#/socialGraph');
                });

                it('should render socialGraph with nodes and links', function() {
                    expect(element('social-graph').count()).toMatch(1);
                    expect(element('social-graph svg').count()).toMatch(1);

                    // Give it some time to render the graph
                    /**
                     * TODO: the next tests should actually be .toBe(4) and .toBe(1) but for some
                     * reason the next describe section is executed first. So we already
                     * added a new activity link...
                     */
                    expect(element('social-graph svg circle').count()).toBeGreaterThan(3);
                    expect(element('social-graph svg line').count()).toBeGreaterThan(0);

                    // Should have multiple dummy nodes
                    expect(element('social-graph svg circle.dummy').count()).toBeGreaterThan(1);
                });

                describe('activity link form', function() {
                    var someDummy;
                    beforeEach(function() {
                        someDummy = element('social-graph svg circle.dummy:first');
                    });

                    it('should go to /activity when clicking dummy node twice', function() {
                        // On the first click, stay on socialGrpah
                        someDummy.click();
                        expect(browser().location().url()).toBe('/socialGraph');

                        // On the second click, go to activity
                        someDummy.click();
                        expect(browser().location().url()).toBe('/activity');
                    });

                    it('should be possible to add a new activity link with a dummy node', function() {
                        someDummy.click();
                        someDummy.click();

                        input('form.targetName').enter('Hans');
                        select('form.selectedActivity').option('Buy something');
                        input('form.location').enter('Bern');
                        input('form.startTime').enter('2014-02-15');

                        element('[ng-view] form button').click();

                        // Should have a success message
                        expect(element('.alert-success').count()).toBe(1);

                        // Check that the social graph has one more node than before
                        browser().navigateTo('#/socialGraph');
                        expect(element('social-graph svg circle').count()).toBe(5); // TODO: this is messed up because the reference code was already entered. darn, can't it just do stuff in the order i say
                    });
                });
            });

            describe('activity link redirect', function() {
                beforeEach(function() {
                    browser().navigateTo('#/activity');
                });

                it('should redirect to /socialGraph when navigating directly to /activity', function() {
                    expect(browser().location().url()).toBe('/socialGraph');
                });

                // TODO: describe the /activity form more
            });
        });
    });

    describe('enter reference code when logged out', function() {
        it('should have a reference code input field', function() {
            expect(input('form.referenceCode').val()).toBe('');
        });

        it('should show the graph when entering a valid reference code', function() {
            input('form.referenceCode').enter('OiWCrB');
            element('.referenceCodeForm button').click();

            // Should show a social graph with two nodes and a connection between them
            expect(browser().location().url()).toBe('/socialGraph');
            expect(element('social-graph svg circle').count()).toBe(2);
            expect(element('social-graph svg line').count()).toBe(1);
        });
    });
});
<commit_msg>Adapting e2e scenarios to the new fixtures from the monkey-tail.<commit_after>/* global describe, beforeEach, it, expect, browser, element, input, select */
'use strict';

// http://docs.angularjs.org/guide/dev_guide.e2e-testing
// This scenario has to be run while monkey-tail is running with
// the fixtures in test/fixtures.
describe('my app', function() {
    beforeEach(function() {
        browser().navigateTo('/app/index.html');

        // TODO: not so great to logout before every test
        element('button.navLogout').click();
    });

    it('should redirect to /login when location hash is empty', function() {
        expect(browser().location().url()).toBe('/login');
    });

    it('should redirect to /login for pages needing authentication', function() {
        browser().navigateTo('#/socialGraph');
        expect(browser().location().url()).toBe('/login');

        browser().navigateTo('#/activity');
        expect(browser().location().url()).toBe('/login');
    });

    describe('login', function() {
        beforeEach(function() {
            browser().navigateTo('#/login');
        });

        it('should render login form when navigating to /login', function() {
            expect(element('[ng-view] form').count()).toBe(1);
            expect(element('[ng-view] form input[type=password]').count()).toBe(1);
        });

        describe('authenticated user', function() {
            beforeEach(function() {
                input('form.email').enter('foo@bar.baz');
                input('form.password').enter('foobar');
                element('[ng-view] form button').click();
            });

            it('should be possible to login with correct username and pw', function() {
                expect(browser().location().url()).toBe('/socialGraph');
            });

            describe('socialGraph', function() {
                beforeEach(function() {
                    browser().navigateTo('#/socialGraph');
                });

                it('should render socialGraph with nodes and links', function() {
                    expect(element('social-graph').count()).toMatch(1);
                    expect(element('social-graph svg').count()).toMatch(1);

                    // Give it some time to render the graph
                    /**
                     * TODO: the next tests should actually be .toBe(6) and .toBe(3) but for some
                     * reason the next describe section is executed first. So we already
                     * added a new activity link...
                     */
                    expect(element('social-graph svg circle').count()).toBeGreaterThan(5);
                    expect(element('social-graph svg line').count()).toBeGreaterThan(2);

                    // Should have multiple dummy nodes
                    expect(element('social-graph svg circle.dummy').count()).toBeGreaterThan(1);
                });

                describe('activity link form', function() {
                    var someDummy;
                    beforeEach(function() {
                        someDummy = element('social-graph svg circle.dummy:first');
                    });

                    it('should go to /activity when clicking dummy node twice', function() {
                        // On the first click, stay on socialGrpah
                        someDummy.click();
                        expect(browser().location().url()).toBe('/socialGraph');

                        // On the second click, go to activity
                        someDummy.click();
                        expect(browser().location().url()).toBe('/activity');
                    });

                    it('should be possible to add a new activity link with a dummy node', function() {
                        someDummy.click();
                        someDummy.click();

                        input('form.targetName').enter('Hans');
                        select('form.selectedActivity').option('Buy something');
                        input('form.location').enter('Bern');
                        input('form.startTime').enter('2014-02-15');

                        element('[ng-view] form button').click();

                        // Should have a success message
                        expect(element('.alert-success').count()).toBe(1);

                        // Check that the social graph has one more node than before
                        browser().navigateTo('#/socialGraph');
                        expect(element('social-graph svg circle').count()).toBe(7);
                    });
                });
            });

            describe('activity link redirect', function() {
                beforeEach(function() {
                    browser().navigateTo('#/activity');
                });

                it('should redirect to /socialGraph when navigating directly to /activity', function() {
                    expect(browser().location().url()).toBe('/socialGraph');
                });

                // TODO: describe the /activity form more
            });
        });
    });

    describe('enter reference code when logged out', function() {
        it('should have a reference code input field', function() {
            expect(input('form.referenceCode').val()).toBe('');
        });

        it('should show the graph when entering a valid reference code', function() {
            input('form.referenceCode').enter('OiWCrB');
            element('.referenceCodeForm button').click();

            // Should show a social graph with two nodes and a connection between them
            expect(browser().location().url()).toBe('/socialGraph');
            expect(element('social-graph svg circle').count()).toBe(4);
            expect(element('social-graph svg line').count()).toBe(1);
        });
    });
});
<|endoftext|>


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

  function ContentEditController($rootScope, $scope, $routeParams, $q, $timeout, $window, $location, appState, contentResource, entityResource, navigationService, notificationsService, angularHelper, serverValidationManager, contentEditingHelper, treeService, fileManager, formHelper, umbRequestHelper, keyboardService, umbModelMapper, editorState, $http, eventsService, relationResource) {

    var evts = [];

    //setup scope vars
    $scope.defaultButton = null;
    $scope.subButtons = [];

    $scope.page = {};
    $scope.page.loading = false;
    $scope.page.menu = {};
    $scope.page.menu.currentNode = null;
    $scope.page.menu.currentSection = appState.getSectionState("currentSection");
    $scope.page.listViewPath = null;
    $scope.page.isNew = $scope.isNew ? true : false;
    $scope.page.buttonGroupState = "init";
    $scope.allowOpen = true;

    function init(content) {

      createButtons(content);

      editorState.set($scope.content);

      //We fetch all ancestors of the node to generate the footer breadcrumb navigation
      if (!$scope.page.isNew) {
        if (content.parentId && content.parentId !== -1) {
          entityResource.getAncestors(content.id, "document")
            .then(function (anc) {
              $scope.ancestors = anc;
            });
        }
      }

      evts.push(eventsService.on("editors.content.changePublishDate", function (event, args) {
        createButtons(args.node);
      }));

      evts.push(eventsService.on("editors.content.changeUnpublishDate", function (event, args) {
        createButtons(args.node);
      }));

      // We don't get the info tab from the server from version 7.8 so we need to manually add it
      contentEditingHelper.addInfoTab($scope.content.tabs);

    }

    function getNode() {

      $scope.page.loading = true;

      //we are editing so get the content item from the server
      $scope.getMethod()($scope.contentId)
        .then(function (data) {

          $scope.content = data;

          if (data.isChildOfListView && data.trashed === false) {
            $scope.page.listViewPath = ($routeParams.page) ?
              "/content/content/edit/" + data.parentId + "?page=" + $routeParams.page :
              "/content/content/edit/" + data.parentId;
          }

          init($scope.content);

          //in one particular special case, after we've created a new item we redirect back to the edit
          // route but there might be server validation errors in the collection which we need to display
          // after the redirect, so we will bind all subscriptions which will show the server validation errors
          // if there are any and then clear them so the collection no longer persists them.
          serverValidationManager.executeAndClearAllSubscriptions();

          syncTreeNode($scope.content, data.path, true);

          resetLastListPageNumber($scope.content);

          eventsService.emit("content.loaded", { content: $scope.content });

          $scope.page.loading = false;

        });

    }

    function createButtons(content) {
      $scope.page.buttonGroupState = "init";
      var buttons = contentEditingHelper.configureContentEditorButtons({
        create: $scope.page.isNew,
        content: content,
        methods: {
          saveAndPublish: $scope.saveAndPublish,
          sendToPublish: $scope.sendToPublish,
          save: $scope.save,
          unPublish: $scope.unPublish
        }
      });

      $scope.defaultButton = buttons.defaultButton;
      $scope.subButtons = buttons.subButtons;

    }

    /** Syncs the content item to it's tree node - this occurs on first load and after saving */
    function syncTreeNode(content, path, initialLoad) {

      if (!$scope.content.isChildOfListView) {
        navigationService.syncTree({ tree: $scope.treeAlias, path: path.split(","), forceReload: initialLoad !== true }).then(function (syncArgs) {
          $scope.page.menu.currentNode = syncArgs.node;
        });
      }
      else if (initialLoad === true) {

        //it's a child item, just sync the ui node to the parent
        navigationService.syncTree({ tree: $scope.treeAlias, path: path.substring(0, path.lastIndexOf(",")).split(","), forceReload: initialLoad !== true });

        //if this is a child of a list view and it's the initial load of the editor, we need to get the tree node
        // from the server so that we can load in the actions menu.
        umbRequestHelper.resourcePromise(
          $http.get(content.treeNodeUrl),
          'Failed to retrieve data for child node ' + content.id).then(function (node) {
          $scope.page.menu.currentNode = node;
        });
      }
    }

    // This is a helper method to reduce the amount of code repitition for actions: Save, Publish, SendToPublish
    function performSave(args) {
      var deferred = $q.defer();

      $scope.page.buttonGroupState = "busy";

      eventsService.emit("content.saving", { content: $scope.content, action: args.action });

      contentEditingHelper.contentEditorPerformSave({
        statusMessage: args.statusMessage,
        saveMethod: args.saveMethod,
        scope: $scope,
        content: $scope.content,
        action: args.action
      }).then(function (data) {
        //success
        init($scope.content);
        syncTreeNode($scope.content, data.path);

        $scope.page.buttonGroupState = "success";

        deferred.resolve(data);

        eventsService.emit("content.saved", { content: $scope.content, action: args.action });

      }, function (err) {
        //error
        if (err) {
          editorState.set($scope.content);
        }

        $scope.page.buttonGroupState = "error";

        deferred.reject(err);
      });

      return deferred.promise;
    }

    function resetLastListPageNumber(content) {
      // We're using rootScope to store the page number for list views, so if returning to the list
      // we can restore the page.  If we've moved on to edit a piece of content that's not the list or it's children
      // we should remove this so as not to confuse if navigating to a different list
      if (!content.isChildOfListView && !content.isContainer) {
        $rootScope.lastListViewPageViewed = null;
      }
    }

    if ($scope.page.isNew) {

      $scope.page.loading = true;

      //we are creating so get an empty content item
      $scope.getScaffoldMethod()()
        .then(function (data) {

          $scope.content = data;

          init($scope.content);

          resetLastListPageNumber($scope.content);

          $scope.page.loading = false;

          eventsService.emit("content.newReady", { content: $scope.content });

          });
    }
    else {

      getNode();

    }

    $scope.unPublish = function () {

      if (formHelper.submitForm({ scope: $scope, statusMessage: "Unpublishing...", skipValidation: true })) {

        $scope.page.buttonGroupState = "busy";

        eventsService.emit("content.unpublishing", { content: $scope.content });

        contentResource.unPublish($scope.content.id)
          .then(function (data) {

            formHelper.resetForm({ scope: $scope, notifications: data.notifications });

            contentEditingHelper.handleSuccessfulSave({
              scope: $scope,
              savedContent: data,
              rebindCallback: contentEditingHelper.reBindChangedProperties($scope.content, data)
            });

            init($scope.content);

            syncTreeNode($scope.content, data.path);

            $scope.page.buttonGroupState = "success";

            eventsService.emit("content.unpublished", { content: $scope.content });

          }, function(err) {
            formHelper.showNotifications(err.data);
            $scope.page.buttonGroupState = 'error';
          });
      }

    };

    $scope.sendToPublish = function () {
      return performSave({ saveMethod: contentResource.sendToPublish, statusMessage: "Sending...", action: "sendToPublish" });
    };

    $scope.saveAndPublish = function () {
      return performSave({ saveMethod: contentResource.publish, statusMessage: "Publishing...", action: "publish" });
    };

    $scope.save = function () {
      return performSave({ saveMethod: $scope.saveMethod(), statusMessage: "Saving...", action: "save" });
    };

    $scope.preview = function (content) {

      if (!$scope.busy) {

        // Chromes popup blocker will kick in if a window is opened
        // without the initial scoped request. This trick will fix that.
        //
        var previewWindow = $window.open('preview/?init=true&id=' + content.id, 'umbpreview');

        // Build the correct path so both /#/ and #/ work.
        var redirect = Umbraco.Sys.ServerVariables.umbracoSettings.umbracoPath + '/preview/?id=' + content.id;

        //The user cannot save if they don't have access to do that, in which case we just want to preview
        //and that's it otherwise they'll get an unauthorized access message
        if (!_.contains(content.allowedActions, "A")) {
            previewWindow.location.href = redirect;
        }
        else {
            $scope.save().then(function (data) {
                previewWindow.location.href = redirect;
            });
        }
      }
    };

    $scope.restore = function (content) {

      $scope.page.buttonRestore = "busy";

      relationResource.getByChildId(content.id, "relateParentDocumentOnDelete").then(function (data) {

        var relation = null;
        var target = null;
        var error = { headline: "Cannot automatically restore this item", content: "Use the Move menu item to move it manually"};

        if (data.length == 0) {
          notificationsService.error(error.headline, "There is no 'restore' relation found for this node. Use the Move menu item to move it manually.");
          $scope.page.buttonRestore = "error";
          return;
        }

        relation = data[0];

        if (relation.parentId == -1) {
          target = { id: -1, name: "Root" };
          moveNode(content, target);
        } else {
          contentResource.getById(relation.parentId).then(function (data) {
            target = data;

            // make sure the target item isn't in the recycle bin
            if(target.path.indexOf("-20") !== -1) {
              notificationsService.error(error.headline, "The item you want to restore it under (" + target.name + ") is in the recycle bin. Use the Move menu item to move the item manually.");
              $scope.page.buttonRestore = "error";
              return;
            }

            moveNode(content, target);

          }, function (err) {
            $scope.page.buttonRestore = "error";
            notificationsService.error(error.headline, error.content);
          });
        }

      }, function (err) {
        $scope.page.buttonRestore = "error";
        notificationsService.error(error.headline, error.content);
      });

    };

    function moveNode(node, target) {

      contentResource.move({ "parentId": target.id, "id": node.id })
        .then(function (path) {

          // remove the node that we're working on
          if($scope.page.menu.currentNode) {
            treeService.removeNode($scope.page.menu.currentNode);
          }

          // sync the destination node
          navigationService.syncTree({ tree: "content", path: path, forceReload: true, activate: false });

          $scope.page.buttonRestore = "success";
          notificationsService.success("Successfully restored " + node.name + " to " + target.name);

          // reload the node
          getNode();

        }, function (err) {
          $scope.page.buttonRestore = "error";
          notificationsService.error("Cannot automatically restore this item", err);
        });

    }

    //ensure to unregister from all events!
    $scope.$on('$destroy', function () {
      for (var e in evts) {
        eventsService.unsubscribe(evts[e]);
      }
    });

  }

  function createDirective() {

    var directive = {
      restrict: 'E',
      replace: true,
      templateUrl: 'views/components/content/edit.html',
      controller: 'Umbraco.Editors.Content.EditorDirectiveController',
      scope: {
        contentId: "=",
        isNew: "=?",
        treeAlias: "@",
        page: "=?",
        saveMethod: "&",
        getMethod: "&",
        getScaffoldMethod: "&?"
      }
    };

    return directive;

  }

  angular.module('umbraco.directives').controller('Umbraco.Editors.Content.EditorDirectiveController', ContentEditController);
  angular.module('umbraco.directives').directive('contentEditor', createDirective);

})();
<commit_msg>updates to make it all work - event from the notification determines the action<commit_after>(function () {
  'use strict';

  function ContentEditController($rootScope, $scope, $routeParams, $q, $timeout, $window, $location, appState, contentResource, entityResource, navigationService, notificationsService, angularHelper, serverValidationManager, contentEditingHelper, treeService, fileManager, formHelper, umbRequestHelper, keyboardService, umbModelMapper, editorState, $http, eventsService, relationResource) {

    var evts = [];

    //setup scope vars
    $scope.defaultButton = null;
    $scope.subButtons = [];

    $scope.page = {};
    $scope.page.loading = false;
    $scope.page.menu = {};
    $scope.page.menu.currentNode = null;
    $scope.page.menu.currentSection = appState.getSectionState("currentSection");
    $scope.page.listViewPath = null;
    $scope.page.isNew = $scope.isNew ? true : false;
    $scope.page.buttonGroupState = "init";
    $scope.allowOpen = true;

    function init(content) {

      createButtons(content);

      editorState.set($scope.content);

      //We fetch all ancestors of the node to generate the footer breadcrumb navigation
      if (!$scope.page.isNew) {
        if (content.parentId && content.parentId !== -1) {
          entityResource.getAncestors(content.id, "document")
            .then(function (anc) {
              $scope.ancestors = anc;
            });
        }
      }

      evts.push(eventsService.on("editors.content.changePublishDate", function (event, args) {
        createButtons(args.node);
      }));

      evts.push(eventsService.on("editors.content.changeUnpublishDate", function (event, args) {
        createButtons(args.node);
      }));

      // We don't get the info tab from the server from version 7.8 so we need to manually add it
      contentEditingHelper.addInfoTab($scope.content.tabs);

    }

    function getNode() {

      $scope.page.loading = true;

      //we are editing so get the content item from the server
      $scope.getMethod()($scope.contentId)
        .then(function (data) {

          $scope.content = data;

          if (data.isChildOfListView && data.trashed === false) {
            $scope.page.listViewPath = ($routeParams.page) ?
              "/content/content/edit/" + data.parentId + "?page=" + $routeParams.page :
              "/content/content/edit/" + data.parentId;
          }

          init($scope.content);

          //in one particular special case, after we've created a new item we redirect back to the edit
          // route but there might be server validation errors in the collection which we need to display
          // after the redirect, so we will bind all subscriptions which will show the server validation errors
          // if there are any and then clear them so the collection no longer persists them.
          serverValidationManager.executeAndClearAllSubscriptions();

          syncTreeNode($scope.content, data.path, true);

          resetLastListPageNumber($scope.content);

          eventsService.emit("content.loaded", { content: $scope.content });

          $scope.page.loading = false;

        });

    }

    function createButtons(content) {
      $scope.page.buttonGroupState = "init";
      var buttons = contentEditingHelper.configureContentEditorButtons({
        create: $scope.page.isNew,
        content: content,
        methods: {
          saveAndPublish: $scope.saveAndPublish,
          sendToPublish: $scope.sendToPublish,
          save: $scope.save,
          unPublish: $scope.unPublish
        }
      });

      $scope.defaultButton = buttons.defaultButton;
      $scope.subButtons = buttons.subButtons;

    }

    /** Syncs the content item to it's tree node - this occurs on first load and after saving */
    function syncTreeNode(content, path, initialLoad) {

      if (!$scope.content.isChildOfListView) {
        navigationService.syncTree({ tree: $scope.treeAlias, path: path.split(","), forceReload: initialLoad !== true }).then(function (syncArgs) {
          $scope.page.menu.currentNode = syncArgs.node;
        });
      }
      else if (initialLoad === true) {

        //it's a child item, just sync the ui node to the parent
        navigationService.syncTree({ tree: $scope.treeAlias, path: path.substring(0, path.lastIndexOf(",")).split(","), forceReload: initialLoad !== true });

        //if this is a child of a list view and it's the initial load of the editor, we need to get the tree node
        // from the server so that we can load in the actions menu.
        umbRequestHelper.resourcePromise(
          $http.get(content.treeNodeUrl),
          'Failed to retrieve data for child node ' + content.id).then(function (node) {
          $scope.page.menu.currentNode = node;
        });
      }
    }

    // This is a helper method to reduce the amount of code repitition for actions: Save, Publish, SendToPublish
    function performSave(args) {
      var deferred = $q.defer();

      $scope.page.buttonGroupState = "busy";

      eventsService.emit("content.saving", { content: $scope.content, action: args.action });

      contentEditingHelper.contentEditorPerformSave({
        statusMessage: args.statusMessage,
        saveMethod: args.saveMethod,
        scope: $scope,
        content: $scope.content,
        action: args.action
      }).then(function (data) {
        //success
        init($scope.content);
        syncTreeNode($scope.content, data.path);

        $scope.page.buttonGroupState = "success";

        deferred.resolve(data);

        eventsService.emit("content.saved", { content: $scope.content, action: args.action });

      }, function (err) {
        //error
        if (err) {
          editorState.set($scope.content);
        }

        $scope.page.buttonGroupState = "error";

        deferred.reject(err);
      });

      return deferred.promise;
    }

    function resetLastListPageNumber(content) {
      // We're using rootScope to store the page number for list views, so if returning to the list
      // we can restore the page.  If we've moved on to edit a piece of content that's not the list or it's children
      // we should remove this so as not to confuse if navigating to a different list
      if (!content.isChildOfListView && !content.isContainer) {
        $rootScope.lastListViewPageViewed = null;
      }
    }

    if ($scope.page.isNew) {

      $scope.page.loading = true;

      //we are creating so get an empty content item
      $scope.getScaffoldMethod()()
        .then(function (data) {

          $scope.content = data;

          init($scope.content);

          resetLastListPageNumber($scope.content);

          $scope.page.loading = false;

          eventsService.emit("content.newReady", { content: $scope.content });

          });
    }
    else {

      getNode();

    }

    $scope.unPublish = function () {
			// raising the event triggers the confirmation dialog
			if (!notificationsService.hasView()) {
				notificationsService.add({ view: "confirmunpublish" });
			}

			$scope.page.buttonGroupState = "busy";

			// actioning the dialog raises the confirmUnpublish event, act on it here
			var actioned = $rootScope.$on("content.confirmUnpublish", function(event, confirmed) {
				if (confirmed && formHelper.submitForm({ scope: $scope, statusMessage: "Unpublishing...", skipValidation: true })) {

					eventsService.emit("content.unpublishing", { content: $scope.content });

					contentResource.unPublish($scope.content.id)
						.then(function (data) {

							formHelper.resetForm({ scope: $scope, notifications: data.notifications });

							contentEditingHelper.handleSuccessfulSave({
								scope: $scope,
								savedContent: data,
								rebindCallback: contentEditingHelper.reBindChangedProperties($scope.content, data)
							});

							init($scope.content);

							syncTreeNode($scope.content, data.path);

							$scope.page.buttonGroupState = "success";
							eventsService.emit("content.unpublished", { content: $scope.content });

						}, function(err) {
							formHelper.showNotifications(err.data);
							$scope.page.buttonGroupState = 'error';
						});

				} else {
					$scope.page.buttonGroupState = "init";
				}

				// unsubscribe to avoid queueing notifications
				// listener is re-bound when the unpublish button is clicked so it is created just-in-time
				actioned();

			});
    };

    $scope.sendToPublish = function () {
      return performSave({ saveMethod: contentResource.sendToPublish, statusMessage: "Sending...", action: "sendToPublish" });
    };

    $scope.saveAndPublish = function () {
      return performSave({ saveMethod: contentResource.publish, statusMessage: "Publishing...", action: "publish" });
    };

    $scope.save = function () {
      return performSave({ saveMethod: $scope.saveMethod(), statusMessage: "Saving...", action: "save" });
    };

    $scope.preview = function (content) {

      if (!$scope.busy) {

        // Chromes popup blocker will kick in if a window is opened
        // without the initial scoped request. This trick will fix that.
        //
        var previewWindow = $window.open('preview/?init=true&id=' + content.id, 'umbpreview');

        // Build the correct path so both /#/ and #/ work.
        var redirect = Umbraco.Sys.ServerVariables.umbracoSettings.umbracoPath + '/preview/?id=' + content.id;

        //The user cannot save if they don't have access to do that, in which case we just want to preview
        //and that's it otherwise they'll get an unauthorized access message
        if (!_.contains(content.allowedActions, "A")) {
            previewWindow.location.href = redirect;
        }
        else {
            $scope.save().then(function (data) {
                previewWindow.location.href = redirect;
            });
        }
      }
    };

    $scope.restore = function (content) {

      $scope.page.buttonRestore = "busy";

      relationResource.getByChildId(content.id, "relateParentDocumentOnDelete").then(function (data) {

        var relation = null;
        var target = null;
        var error = { headline: "Cannot automatically restore this item", content: "Use the Move menu item to move it manually"};

        if (data.length == 0) {
          notificationsService.error(error.headline, "There is no 'restore' relation found for this node. Use the Move menu item to move it manually.");
          $scope.page.buttonRestore = "error";
          return;
        }

        relation = data[0];

        if (relation.parentId == -1) {
          target = { id: -1, name: "Root" };
          moveNode(content, target);
        } else {
          contentResource.getById(relation.parentId).then(function (data) {
            target = data;

            // make sure the target item isn't in the recycle bin
            if(target.path.indexOf("-20") !== -1) {
              notificationsService.error(error.headline, "The item you want to restore it under (" + target.name + ") is in the recycle bin. Use the Move menu item to move the item manually.");
              $scope.page.buttonRestore = "error";
              return;
            }

            moveNode(content, target);

          }, function (err) {
            $scope.page.buttonRestore = "error";
            notificationsService.error(error.headline, error.content);
          });
        }

      }, function (err) {
        $scope.page.buttonRestore = "error";
        notificationsService.error(error.headline, error.content);
      });

    };

    function moveNode(node, target) {

      contentResource.move({ "parentId": target.id, "id": node.id })
        .then(function (path) {

          // remove the node that we're working on
          if($scope.page.menu.currentNode) {
            treeService.removeNode($scope.page.menu.currentNode);
          }

          // sync the destination node
          navigationService.syncTree({ tree: "content", path: path, forceReload: true, activate: false });

          $scope.page.buttonRestore = "success";
          notificationsService.success("Successfully restored " + node.name + " to " + target.name);

          // reload the node
          getNode();

        }, function (err) {
          $scope.page.buttonRestore = "error";
          notificationsService.error("Cannot automatically restore this item", err);
        });

    }

    //ensure to unregister from all events!
    $scope.$on('$destroy', function () {
      for (var e in evts) {
        eventsService.unsubscribe(evts[e]);
      }
    });

  }

  function createDirective() {

    var directive = {
      restrict: 'E',
      replace: true,
      templateUrl: 'views/components/content/edit.html',
      controller: 'Umbraco.Editors.Content.EditorDirectiveController',
      scope: {
        contentId: "=",
        isNew: "=?",
        treeAlias: "@",
        page: "=?",
        saveMethod: "&",
        getMethod: "&",
        getScaffoldMethod: "&?"
      }
    };

    return directive;

  }

  angular.module('umbraco.directives').controller('Umbraco.Editors.Content.EditorDirectiveController', ContentEditController);
  angular.module('umbraco.directives').directive('contentEditor', createDirective);

})();
<|endoftext|>


<commit_before>'use strict';

/* http://docs.angularjs.org/guide/dev_guide.e2e-testing */
describe('Angular Timer E2E Tests', function () {

  angular.scenario.dsl('value', function() {
    return function(value) {
      return this.addFuture('value to future', function(done) {
        done(null, value);
      });
    };
  });

  beforeEach(function () {
    browser().navigateTo('/index.html');
  });

  it("Simple Timer - Should stop ticking when user clicks 'Stop' button", function () {
    sleep(1);
    element('#basic-timer button:last-child').click();
    var oldValue = null;
    element('#basic-timer timer span').query(function (span, done) {
      oldValue = span.html();
      done();
    });
    sleep(1);
    element('#basic-timer timer span').query(function (span, done) {
      expect(value(oldValue)).toBe(span.html());
      done();
    });
  });

  it('Simple Timer - Should reset timer value when user click on start button again', function () {
    sleep(2);
    var oldValue = element('#basic-timer timer span').text();
    element('#basic-timer button:nth-child(3)').click();
    expect(element('#basic-timer span').text()).toBeLessThan(oldValue);
  });

  it('Clock Timer - with hours, minutes & seconds', function () {
    expect(element('#clock-timer timer').html()).toMatch(/0 hours,/);
    expect(element('#clock-timer timer').html()).toMatch(/0 minutes,/);
    expect(element('#clock-timer timer').html()).toMatch(/seconds./);
  });

  it('Countdown Timer - Starts from 100', function () {
    expect(element('#countdown-timer timer').html()).toBeLessThan(100);
  });

  it('Autostart False Timer - Init from 0', function () {
    expect(element('#auto-start-false-timer timer').html()).toBe('0');
    element('#auto-start-false-timer button:nth-child(3)').click();
    sleep(2);
    expect(element('#auto-start-false-timer timer').html()).toBeLessThan(100);
  });
});
<commit_msg>minor refactoring<commit_after>'use strict';

/* http://docs.angularjs.org/guide/dev_guide.e2e-testing */
describe('Angular Timer E2E Tests', function () {

  angular.scenario.dsl('futureValue', function() {
    return function(value) {
      return this.addFuture('value to future', function(done) {
        done(null, value);
      });
    };
  });

  beforeEach(function () {
    browser().navigateTo('/index.html');
  });

  it("Simple Timer - Should stop ticking when user clicks 'Stop' button", function () {
    sleep(1);
    element('#basic-timer button:last-child').click();
    var oldValue = null;
    element('#basic-timer timer span').query(function (span, done) {
      oldValue = span.html();
      done();
    });
    sleep(1);
    element('#basic-timer timer span').query(function (span, done) {
      expect(futureValue(oldValue)).toBe(span.html());
      done();
    });
  });

  it('Simple Timer - Should reset timer value when user click on start button again', function () {
    sleep(2);
    var oldValue = element('#basic-timer timer span').text();
    element('#basic-timer button:nth-child(3)').click();
    expect(element('#basic-timer span').text()).toBeLessThan(oldValue);
  });

  it('Clock Timer - with hours, minutes & seconds', function () {
    expect(element('#clock-timer timer').html()).toMatch(/0 hours,/);
    expect(element('#clock-timer timer').html()).toMatch(/0 minutes,/);
    expect(element('#clock-timer timer').html()).toMatch(/seconds./);
  });

  it('Countdown Timer - Starts from 100', function () {
    expect(element('#countdown-timer timer').html()).toBeLessThan(100);
  });

  it('Autostart False Timer - Init from 0', function () {
    expect(element('#auto-start-false-timer timer').html()).toBe('0');
    element('#auto-start-false-timer button:nth-child(3)').click();
    sleep(2);
    expect(element('#auto-start-false-timer timer').html()).toBeGreaterThan(0);
    expect(element('#auto-start-false-timer timer').html()).toBeLessThan(100);
  });
});
<|endoftext|>