<commit_before>/*jshint undef: true, browser:true, nomen: true */
/*jslint browser:true, nomen: true */
/*global mx, define, require, console, google */
/*
    GeoLocationWidget
    ========================

    @file      : GeoLocationWidget.js
    @version   : 1.0
    @author    : Marcel Groeneweg
    @date      : Sun, 11 Oct 2015 07:05:38 GMT
    @copyright :
    @license   : Apache 2

    Documentation
    ========================
    Use Google client library to get current location. The Google client library takes WiFi access points into account to determine the location.
    This widget depends on the original GoogleMaps widget for the AMD loader.
*/

// Required module list. Remove unnecessary modules, you can always get them back from the boilerplate.
define([
    "dojo/_base/declare",
    "mxui/widget/_WidgetBase",
    "dojo/dom-class",
    "dojo/dom-style",
    "dojo/_base/lang",
    "dojo/html",
    "dojo/_base/event",
    "GoogleMaps/lib/googlemaps!"
], function (declare, _WidgetBase, dojoClass, dojoStyle, dojoLang, dojoHtml, dojoEvent, googleMaps) {
    "use strict";

    // Declare widget's prototype.
    return declare("GeoLocationWidget.widget.GeoLocationWidget", [ _WidgetBase ], {

        // DOM elements

        // Parameters configured in the Modeler.
        buttonLabel: "",
        buttonClass: "",
        latAttr: "",
        longAttr: "",
        onChangeMF: "",
        doReverseGeocoding: true,
        reverseGeocodingResultJson: "",

        // Internal variables. Non-primitives created in the prototype are shared between all widget instances.
        _handles: null,
        _contextObj: null,
        _result: null,
        _button: null,
        _mapContainer: null,
        _googleMap: null,
        _marker: null,

        // dojo.declare.constructor is called to construct the widget instance. Implement to initialize non-primitive properties.
        constructor: function () {
            this._handles = [];
        },

        // dijit._WidgetBase.postCreate is called after constructing the widget. Implement to do extra setup work.
        postCreate: function () {
            //console.log(this.id + ".postCreate");

            // Placeholder container
            this._mapContainer = document.createElement("div");
            if (this.mapClass) {
                dojoClass.add(this._mapContainer, this.mapClass);
            } else {
                dojoStyle.set(this._mapContainer, {
                    height: "350px",
                    width: "100%"
                });
            }
            // Add to widget node
            this.domNode.appendChild(this._mapContainer);

            // Button
            this._button = document.createElement("div");
            dojoClass.add(this._button, "btn");
            if (this.buttonClass) {
                dojoClass.add(this._button, this.buttonClass);
            } else {
                dojoClass.add(this._button, "btn-primary");
            }

            dojoHtml.set(this._button, this.buttonLabel || "GEO Location");

            // Add to widget node
            this.domNode.appendChild(this._button);

            this._updateRendering();
            this._setupEvents();

            if (google.maps) {
                this._loadMap();
            } else {
                window[this.id + "_mapsCallback"] = dojoLang.hitch(this, function () {
                    this._loadMap();
                });
            }
        },

        // mxui.widget._WidgetBase.update is called when context is changed or initialized. Implement to re-render and / or fetch data.
        update: function (obj, callback) {
            //console.log(this.id + ".update");

            this._contextObj = obj;
            this._resetSubscriptions();
            this._updateRendering();

            callback();
        },

        // mxui.widget._WidgetBase.enable is called when the widget should enable editing. Implement to enable editing if widget is input widget.
        enable: function () {},

        // mxui.widget._WidgetBase.enable is called when the widget should disable editing. Implement to disable editing if widget is input widget.
        disable: function () {},

        // mxui.widget._WidgetBase.resize is called when the page's layout is recalculated. Implement to do sizing calculations. Prefer using CSS instead.
        resize: function (box) {
            if (this._googleMap) {
                google.maps.event.trigger(this._googleMap, "resize");
            }
        },

        // mxui.widget._WidgetBase.uninitialize is called when the widget is destroyed. Implement to do special tear-down work.
        uninitialize: function () {
            // Clean up listeners, helper objects, etc. There is no need to remove listeners added with this.connect / this.subscribe / this.own.
            this._marker.setMap(null);
            this._marker = null;

            window[this.id + "_mapsCallback"] = null;
        },

        // We want to stop events on a mobile device
        _stopBubblingEventOnMobile: function (e) {
            if (typeof document.ontouchstart !== "undefined") {
                dojoEvent.stop(e);
            }
        },

        // Attach events to HTML dom elements
        _setupEvents: function () {
            this.connect(this._button, "click", dojoLang.hitch(this, function (evt) {
                //console.log("GEO Location start getting location.");

                // The camera function has a success, failure and a reference to this.
                navigator.geolocation.getCurrentPosition(
                    dojoLang.hitch(this, this._geolocationSucces),
                    dojoLang.hitch(this, this._geolocationFailure),
                    {timeout: 10000, enableHighAccuracy: true}
                );
            }));
        },

        // Rerender the interface.
        _updateRendering: function () {
            if (this._googleMap) {
                this._showMarker();
                google.maps.event.trigger(this._googleMap, "resize");
            }
        },

        _geolocationSucces: function (position) {
            this._setCurrentLocation(position.coords.latitude, position.coords.longitude);
        },

        _setCurrentLocation: function (latitude, longitude) {

            var geocoder,
                latlng;

            this._contextObj.set(this.latAttr, latitude.toFixed(8));
            this._contextObj.set(this.longAttr, longitude.toFixed(8));
            this._showMarker();
            if (this.reverseGeoCodingAction === "locationOnly") {
                this._executeMicroflow();
            } else {
                geocoder = new google.maps.Geocoder();
                latlng = {lat: latitude, lng: longitude};
                geocoder.geocode({
                    "location": latlng
                }, dojoLang.hitch(this, this._reverseGeoCodingSuccess));
            }
        },

        _showMarker: function () {
            var latLng;

            if (this._marker === null) {
                this._marker = new google.maps.Marker({
                    map: this._googleMap,
                    draggable: true
                });
                // Register Custom "dragend" Event
                google.maps.event.addListener(this._marker, "dragend", dojoLang.hitch(this, function () {
                    // Get the position where the pointer was dropped
                    var point = this._marker.getPosition();
                    // Move map to marker location
                    this._setCurrentLocation(point.lat(), point.lng());
                }));
            }
            latLng = this._createLatLng();
            this._marker.setPosition(latLng);
            this._googleMap.panTo(latLng);
            if (this.markerDisplayAttr) {
                this._marker.setTitle(this._contextObj.get(this.markerDisplayAttr));
            }
        },

        _reverseGeoCodingSuccess: function (results, status) {
            var resultString;

            if (status === google.maps.GeocoderStatus.OK) {
                console.log("Reverse geocode OK");
                //console.dir(results);
                if (this.reverseGeoCodingAction === "fullResult") {
                    resultString = JSON.stringify(results);
                    this._contextObj.set(this.reverseGeocodingResultJson, resultString);
                } else {
                    if (results && results.length && results[0]) {
                        this._contextObj.set(this.address, results[0].formatted_address);
                    }
                }
                this._executeMicroflow();
            } else {
                window.alert("Geocoder failed due to: " + status);
            }
        },

        _geolocationFailure: function (error) {

            //console.log("GEO Location failure!");
            //console.log(error.message);

            if (this._result) {
                dojoHtml.set(this._result, "GEO Location failure...");
            } else {
                this._result = document.createElement("div");
                dojoHtml.set(this._result, "GEO Location failure...");
                this.domNode.appendChild(this._result);
            }
        },

        _loadMap: function () {

            this._googleMap = new google.maps.Map(this._mapContainer, {
                zoom: this.defaultZoom,
                center: new google.maps.LatLng(this.defaultLat, this.defaultLng),
                mapTypeId: google.maps.MapTypeId.ROADMAP,
                mapTypeControlOption: {
                    style: google.maps.MapTypeControlStyle.HORIZONTAL_BAR
                }
            });

        },

        _createLatLng: function () {
            var lat,
                lng;

            if (this._contextObj) {
                lat = this._contextObj.get(this.latAttr).toFixed(8);
                lng = this._contextObj.get(this.longAttr).toFixed(8);
            }
            if (lat === null || lat === undefined) {
                lat = 0;
            }
            if (lng === null || lng === undefined) {
                lng = 0;
            }

            return new google.maps.LatLng(lat, lng);
        },

        _executeMicroflow: function () {

            if (this.onChangeMF && this._contextObj) {
                mx.processor.xasAction({
                    error       : function () {},
                    actionname  : this.onChangeMF,
                    applyto     : "selection",
                    guids       : [this._contextObj.getGuid()]
                });
            }
        },

        // Reset subscriptions.
        _resetSubscriptions: function () {
            // Release handles on previous object, if any.
            if (this._handles) {
                this._handles.forEach(function (handle) {
                    mx.data.unsubscribe(handle);
                });
                this._handles = [];
            }

            // When a mendix object exists create subscribtions.
            if (this._contextObj) {
                var objectHandle = this.subscribe({
                    guid: this._contextObj.getGuid(),
                    callback: dojoLang.hitch(this, function (guid) {
                        this._updateRendering();
                    })
                });

                this._handles = [ objectHandle ];
            }
        }
    });
});

require(["GeoLocationWidget/widget/GeoLocationWidget"], function () {
    "use strict";
});
<commit_msg>Remove GoogleMaps module dependency and add load Maps from Google<commit_after>/*jshint undef: true, browser:true, nomen: true */
/*jslint browser:true, nomen: true */
/*global mx, define, require, console, google */
/*
    GeoLocationWidget
    ========================

    @file      : GeoLocationWidget.js
    @version   : 1.0
    @author    : Marcel Groeneweg
    @date      : Sun, 11 Oct 2015 07:05:38 GMT
    @copyright :
    @license   : Apache 2

    Documentation
    ========================
    Use Google client library to get current location. The Google client library takes WiFi access points into account to determine the location.
    This widget depends on the original GoogleMaps widget for the AMD loader.
*/

// Required module list. Remove unnecessary modules, you can always get them back from the boilerplate.
define([
    "dojo/_base/declare",
    "mxui/widget/_WidgetBase",
    "dojo/dom-class",
    "dojo/dom-style",
    "dojo/_base/lang",
    "dojo/html",
    "dojo/_base/event",
    "//www.google.com/jsapi"
], function (declare, _WidgetBase, dojoClass, dojoStyle, dojoLang, dojoHtml, dojoEvent) {
    "use strict";

    // Declare widget's prototype.
    return declare("GeoLocationWidget.widget.GeoLocationWidget", [ _WidgetBase ], {

        // DOM elements

        // Parameters configured in the Modeler.
        buttonLabel: "",
        buttonClass: "",
        latAttr: "",
        longAttr: "",
        onChangeMF: "",
        doReverseGeocoding: true,
        reverseGeocodingResultJson: "",
        apiKey: "",

        // Internal variables. Non-primitives created in the prototype are shared between all widget instances.
        _handles: null,
        _contextObj: null,
        _result: null,
        _button: null,
        _mapContainer: null,
        _googleMap: null,
        _marker: null,

        // dojo.declare.constructor is called to construct the widget instance. Implement to initialize non-primitive properties.
        constructor: function () {
            this._handles = [];
        },

        // dijit._WidgetBase.postCreate is called after constructing the widget. Implement to do extra setup work.
        postCreate: function () {
            //console.log(this.id + ".postCreate");

            if (google && !google.maps) {
                var params = "sensor=true";
                if (this.apiKey !== "") {
                    params += "&key=" + this.apiKey;
                }
                google.load("maps", 3, {
                    other_params: params,
                    callback: dojoLang.hitch(this, this.setupWidget)
                });
            } else if (google && google.maps) {
                dojoLang.hitch(this, this.setupWidget);
            }
        },

        setupWidget: function () {

            // Placeholder container
            this._mapContainer = document.createElement("div");
            if (this.mapClass) {
                dojoClass.add(this._mapContainer, this.mapClass);
            } else {
                dojoStyle.set(this._mapContainer, {
                    height: "350px",
                    width: "100%"
                });
            }
            // Add to widget node
            this.domNode.appendChild(this._mapContainer);

            // Button
            this._button = document.createElement("div");
            dojoClass.add(this._button, "btn");
            if (this.buttonClass) {
                dojoClass.add(this._button, this.buttonClass);
            } else {
                dojoClass.add(this._button, "btn-primary");
            }

            dojoHtml.set(this._button, this.buttonLabel || "GEO Location");

            // Add to widget node
            this.domNode.appendChild(this._button);

            this._updateRendering();
            this._setupEvents();

            if (google.maps) {
                this._loadMap();
            } else {
                window[this.id + "_mapsCallback"] = dojoLang.hitch(this, function () {
                    this._loadMap();
                });
            }
        },

        // mxui.widget._WidgetBase.update is called when context is changed or initialized. Implement to re-render and / or fetch data.
        update: function (obj, callback) {
            //console.log(this.id + ".update");

            this._contextObj = obj;
            this._resetSubscriptions();
            this._updateRendering();

            callback();
        },

        // mxui.widget._WidgetBase.enable is called when the widget should enable editing. Implement to enable editing if widget is input widget.
        enable: function () {},

        // mxui.widget._WidgetBase.enable is called when the widget should disable editing. Implement to disable editing if widget is input widget.
        disable: function () {},

        // mxui.widget._WidgetBase.resize is called when the page's layout is recalculated. Implement to do sizing calculations. Prefer using CSS instead.
        resize: function (box) {
            if (this._googleMap) {
                google.maps.event.trigger(this._googleMap, "resize");
            }
        },

        // mxui.widget._WidgetBase.uninitialize is called when the widget is destroyed. Implement to do special tear-down work.
        uninitialize: function () {
            // Clean up listeners, helper objects, etc. There is no need to remove listeners added with this.connect / this.subscribe / this.own.
            this._marker.setMap(null);
            this._marker = null;

            window[this.id + "_mapsCallback"] = null;
        },

        // We want to stop events on a mobile device
        _stopBubblingEventOnMobile: function (e) {
            if (typeof document.ontouchstart !== "undefined") {
                dojoEvent.stop(e);
            }
        },

        // Attach events to HTML dom elements
        _setupEvents: function () {
            this.connect(this._button, "click", dojoLang.hitch(this, function (evt) {
                //console.log("GEO Location start getting location.");

                // The camera function has a success, failure and a reference to this.
                navigator.geolocation.getCurrentPosition(
                    dojoLang.hitch(this, this._geolocationSucces),
                    dojoLang.hitch(this, this._geolocationFailure),
                    {timeout: 10000, enableHighAccuracy: true}
                );
            }));
        },

        // Rerender the interface.
        _updateRendering: function () {
            if (this._googleMap) {
                this._showMarker();
                google.maps.event.trigger(this._googleMap, "resize");
            }
        },

        _geolocationSucces: function (position) {
            this._setCurrentLocation(position.coords.latitude, position.coords.longitude);
        },

        _setCurrentLocation: function (latitude, longitude) {

            var geocoder,
                latlng;

            this._contextObj.set(this.latAttr, latitude.toFixed(8));
            this._contextObj.set(this.longAttr, longitude.toFixed(8));
            this._showMarker();
            if (this.reverseGeoCodingAction === "locationOnly") {
                this._executeMicroflow();
            } else {
                geocoder = new google.maps.Geocoder();
                latlng = {lat: latitude, lng: longitude};
                geocoder.geocode({
                    "location": latlng
                }, dojoLang.hitch(this, this._reverseGeoCodingSuccess));
            }
        },

        _showMarker: function () {
            var latLng;

            if (this._marker === null) {
                this._marker = new google.maps.Marker({
                    map: this._googleMap,
                    draggable: true
                });
                // Register Custom "dragend" Event
                google.maps.event.addListener(this._marker, "dragend", dojoLang.hitch(this, function () {
                    // Get the position where the pointer was dropped
                    var point = this._marker.getPosition();
                    // Move map to marker location
                    this._setCurrentLocation(point.lat(), point.lng());
                }));
            }
            latLng = this._createLatLng();
            this._marker.setPosition(latLng);
            this._googleMap.panTo(latLng);
            if (this.markerDisplayAttr) {
                this._marker.setTitle(this._contextObj.get(this.markerDisplayAttr));
            }
        },

        _reverseGeoCodingSuccess: function (results, status) {
            var resultString;

            if (status === google.maps.GeocoderStatus.OK) {
                console.log("Reverse geocode OK");
                //console.dir(results);
                if (this.reverseGeoCodingAction === "fullResult") {
                    resultString = JSON.stringify(results);
                    this._contextObj.set(this.reverseGeocodingResultJson, resultString);
                } else {
                    if (results && results.length && results[0]) {
                        this._contextObj.set(this.address, results[0].formatted_address);
                    }
                }
                this._executeMicroflow();
            } else {
                window.alert("Geocoder failed due to: " + status);
            }
        },

        _geolocationFailure: function (error) {

            //console.log("GEO Location failure!");
            //console.log(error.message);

            if (this._result) {
                dojoHtml.set(this._result, "GEO Location failure...");
            } else {
                this._result = document.createElement("div");
                dojoHtml.set(this._result, "GEO Location failure...");
                this.domNode.appendChild(this._result);
            }
        },

        _loadMap: function () {

            this._googleMap = new google.maps.Map(this._mapContainer, {
                zoom: this.defaultZoom,
                center: new google.maps.LatLng(this.defaultLat, this.defaultLng),
                mapTypeId: google.maps.MapTypeId.ROADMAP,
                mapTypeControlOption: {
                    style: google.maps.MapTypeControlStyle.HORIZONTAL_BAR
                }
            });

        },

        _createLatLng: function () {
            var lat,
                lng;

            if (this._contextObj) {
                lat = this._contextObj.get(this.latAttr).toFixed(8);
                lng = this._contextObj.get(this.longAttr).toFixed(8);
            }
            if (lat === null || lat === undefined) {
                lat = 0;
            }
            if (lng === null || lng === undefined) {
                lng = 0;
            }

            return new google.maps.LatLng(lat, lng);
        },

        _executeMicroflow: function () {

            if (this.onChangeMF && this._contextObj) {
                mx.processor.xasAction({
                    error       : function () {},
                    actionname  : this.onChangeMF,
                    applyto     : "selection",
                    guids       : [this._contextObj.getGuid()]
                });
            }
        },

        // Reset subscriptions.
        _resetSubscriptions: function () {
            // Release handles on previous object, if any.
            if (this._handles) {
                this._handles.forEach(function (handle) {
                    mx.data.unsubscribe(handle);
                });
                this._handles = [];
            }

            // When a mendix object exists create subscribtions.
            if (this._contextObj) {
                var objectHandle = this.subscribe({
                    guid: this._contextObj.getGuid(),
                    callback: dojoLang.hitch(this, function (guid) {
                        this._updateRendering();
                    })
                });

                this._handles = [ objectHandle ];
            }
        }
    });
});

require(["GeoLocationWidget/widget/GeoLocationWidget"], function () {
    "use strict";
});
<|endoftext|>


<commit_before>/* eslint-disable no-console, no-underscore-dangle */
/* globals window */
import _ from 'lodash';
import React, { PropTypes } from 'react';

// @TODO: check for an alternative to remove global dependency
function getWidgets(name) {
  return window.app.getWidgets(name);
}

function getObservable$(name) {
  return window.app.observeWidgets$(name);
}

function isRootAppAvailable() {
  return window.app;
}

export default React.createClass({
  propTypes: {
    name: PropTypes.string.isRequired
  },

  /**
   * Determines if the the component should be updated or not.
   * Since we are calling setState multiple times, we need to make sure that only when
   * the list of widgets to render, i.e. this.state.listForRendering, is changed should
   * trigger a re-render of the region component.
   * @param  {Object}  nextProps  the next set of props
   * @param  {Object}  nextState  the next component state to be set
   * @return {Boolean} a boolean flag indicating whether the component should be updated
   */
  shouldComponentUpdate(nextProps, nextState) {
    let shouldUpdate = !_.isEqual(this.props, nextProps);
    if (!shouldUpdate) {
      const { listForRendering } = nextState;
      shouldUpdate = shouldUpdate || this.state.listForRendering.length !== listForRendering.length;
      shouldUpdate = shouldUpdate ||
        _.zipWith(this.state.listForRendering, listForRendering, (prev, next) => prev.name === next.name)
          .some(value => !value);
    }
    return shouldUpdate;
  },

  componentWillMount() {
    if (!isRootAppAvailable()) {
      return;
    }

    const observable = getObservable$();

    this.subscription = observable.subscribe({
      // @TODO: this can be optimized further
      next: () => {
        this.setState({
          list: getWidgets(this.props.name)
        }, () => {
          this.state.list.forEach((widget) => {
            const appName = widget.getOption('appName');
            const widgetName = widget.getOption('name');

            const existsInState = this.state.listForRendering.some((item) => {
              return (item.appName === appName) && (item.name === widgetName);
            });

            if (existsInState) {
              return;
            }

            const WidgetComponent = widget.render();
            const WrapperComponent = React.createClass({
              componentWillMount() {
                widget.beforeMount();
              },

              componentDidMount() {
                widget.afterMount();
              },

              componentWillUnmount() {
                widget.beforeUnmount();
              },

              render() {
                return <WidgetComponent />;
              }
            });

            const listForRendering = [...this.state.listForRendering, {
              appName,
              Component: WrapperComponent,
              name: widgetName,
            }];

            this.setState({ listForRendering });
          });
        });
      },
      error: (err) => {
        console.warn(`Subscription error for <Region name="${this.props.name}" />:`, err);
      }
    });
  },

  componentWillUnmount() {
    this.subscription.unsubscribe();
  },

  getInitialState() {
    return {
      list: [], // array of widgetApps
      listForRendering: [] // array of {name, component} objects
    };
  },

  render() {
    const { listForRendering } = this.state;

    if (listForRendering.length === 0) {
      return null;
    }

    return (

        {listForRendering.map((item) => {
          const { appName, Component, name } = item;

          return (
            <Component key={`${appName}_${name}`} />
          );
        })}

    );
  }
});
<commit_msg>check if `window` exists in Region (#62)<commit_after>/* eslint-disable no-console, no-underscore-dangle */
/* globals window */
import _ from 'lodash';
import React, { PropTypes } from 'react';

// @TODO: check for an alternative to remove global dependency
function getWidgets(name) {
  return window.app.getWidgets(name);
}

function getObservable$(name) {
  return window.app.observeWidgets$(name);
}

function isRootAppAvailable() {
  return (
    typeof window !== 'undefined' &&
    window.app
  );
}

export default React.createClass({
  propTypes: {
    name: PropTypes.string.isRequired
  },

  /**
   * Determines if the the component should be updated or not.
   * Since we are calling setState multiple times, we need to make sure that only when
   * the list of widgets to render, i.e. this.state.listForRendering, is changed should
   * trigger a re-render of the region component.
   * @param  {Object}  nextProps  the next set of props
   * @param  {Object}  nextState  the next component state to be set
   * @return {Boolean} a boolean flag indicating whether the component should be updated
   */
  shouldComponentUpdate(nextProps, nextState) {
    let shouldUpdate = !_.isEqual(this.props, nextProps);
    if (!shouldUpdate) {
      const { listForRendering } = nextState;
      shouldUpdate = shouldUpdate || this.state.listForRendering.length !== listForRendering.length;
      shouldUpdate = shouldUpdate ||
        _.zipWith(this.state.listForRendering, listForRendering, (prev, next) => prev.name === next.name)
          .some(value => !value);
    }
    return shouldUpdate;
  },

  componentWillMount() {
    if (!isRootAppAvailable()) {
      return;
    }

    const observable = getObservable$();

    this.subscription = observable.subscribe({
      // @TODO: this can be optimized further
      next: () => {
        this.setState({
          list: getWidgets(this.props.name)
        }, () => {
          this.state.list.forEach((widget) => {
            const appName = widget.getOption('appName');
            const widgetName = widget.getOption('name');

            const existsInState = this.state.listForRendering.some((item) => {
              return (item.appName === appName) && (item.name === widgetName);
            });

            if (existsInState) {
              return;
            }

            const WidgetComponent = widget.render();
            const WrapperComponent = React.createClass({
              componentWillMount() {
                widget.beforeMount();
              },

              componentDidMount() {
                widget.afterMount();
              },

              componentWillUnmount() {
                widget.beforeUnmount();
              },

              render() {
                return <WidgetComponent />;
              }
            });

            const listForRendering = [...this.state.listForRendering, {
              appName,
              Component: WrapperComponent,
              name: widgetName,
            }];

            this.setState({ listForRendering });
          });
        });
      },
      error: (err) => {
        console.warn(`Subscription error for <Region name="${this.props.name}" />:`, err);
      }
    });
  },

  componentWillUnmount() {
    this.subscription.unsubscribe();
  },

  getInitialState() {
    return {
      list: [], // array of widgetApps
      listForRendering: [] // array of {name, component} objects
    };
  },

  render() {
    const { listForRendering } = this.state;

    if (listForRendering.length === 0) {
      return null;
    }

    return (

        {listForRendering.map((item) => {
          const { appName, Component, name } = item;

          return (
            <Component key={`${appName}_${name}`} />
          );
        })}

    );
  }
});
<|endoftext|>


<commit_before>import React, {Component, PropTypes} from 'react'
import cx from 'classnames'

import './button.scss'

export default class Button extends Component {
	static propTypes = {
		children: PropTypes.any.isRequired,
		className: PropTypes.string,
		disabled: PropTypes.bool,
		onClick: PropTypes.func,
		title: PropTypes.string,
		type: PropTypes.oneOf(['flat', 'raised']).isRequired,
	}

	static defaultProps = {
		type: 'flat',
	}

	render() {
		return (
			<button
				className={cx('button', `button--${this.props.type}`, this.props.className)}
				disabled={this.props.disabled}
				onClick={this.props.onClick}
				title={this.props.title}>
				{this.props.children}
			</button>
		)
	}
}
<commit_msg>always render buttons of type='button'<commit_after>import React, {Component, PropTypes} from 'react'
import cx from 'classnames'

import './button.scss'

export default class Button extends Component {
	static propTypes = {
		children: PropTypes.any.isRequired,
		className: PropTypes.string,
		disabled: PropTypes.bool,
		onClick: PropTypes.func,
		title: PropTypes.string,
		type: PropTypes.oneOf(['flat', 'raised']).isRequired,
	}

	static defaultProps = {
		type: 'flat',
	}

	render() {
		return (
			<button type='button'
				className={cx('button', `button--${this.props.type}`, this.props.className)}
				disabled={this.props.disabled}
				onClick={this.props.onClick}
				title={this.props.title}>
				{this.props.children}
			</button>
		)
	}
}
<|endoftext|>


<commit_before>import React, {Component} from 'react';
import { Link } from 'react-router-dom';
import lsUtils from '../utils/ls_utils';
import constants from '../utils/constants';
import * as actionCreators from '../actions/index';
import ellipsis from 'text-ellipsis';

export default class Header extends Component{

	componentWillMount(){
		// console.log('I am in header componentWillMount');
		let user = lsUtils.getValue(constants.KEY_USER_OBJECT);
		let company = lsUtils.getValue(constants.KEY_COMPANY_OBJECT);
		this.setState({
			user: user,
			company : company
		});
	}

	componentWillReceiveProps(nextProps){
		// console.log('I am in header componentWillReceiveProps');
		let user = lsUtils.getValue(constants.KEY_USER_OBJECT);
		let company = lsUtils.getValue(constants.KEY_COMPANY_OBJECT);
		this.setState({
			user: user,
			company : company
		});
	}

	logoutCurrentUser(){
		// delete all the saved values
		lsUtils.deleteAll();
		window.location.replace("http://bookbild.com");
	}

	displayLaunchButton(){
		if(this.state.user){
			return(
				LAUNCH
			);
		} else {
			return(
				LAUNCH
			);
		}
	}

	render(){
		// console.log('I am in header render');
		return (

						 SOLUTIONS
						 INVITE COMPANY
						 DEMO
						 INSIGHTS
						 TEAM
						 DEAL COMPARISON TOOL
						{this.displayLaunchButton()}

		    );
	}
}
<commit_msg>commenting the deal comparision<commit_after>import React, {Component} from 'react';
import { Link } from 'react-router-dom';
import lsUtils from '../utils/ls_utils';
import constants from '../utils/constants';
import * as actionCreators from '../actions/index';
import ellipsis from 'text-ellipsis';

export default class Header extends Component{

	componentWillMount(){
		// console.log('I am in header componentWillMount');
		let user = lsUtils.getValue(constants.KEY_USER_OBJECT);
		let company = lsUtils.getValue(constants.KEY_COMPANY_OBJECT);
		this.setState({
			user: user,
			company : company
		});
	}

	componentWillReceiveProps(nextProps){
		// console.log('I am in header componentWillReceiveProps');
		let user = lsUtils.getValue(constants.KEY_USER_OBJECT);
		let company = lsUtils.getValue(constants.KEY_COMPANY_OBJECT);
		this.setState({
			user: user,
			company : company
		});
	}

	logoutCurrentUser(){
		// delete all the saved values
		lsUtils.deleteAll();
		window.location.replace("http://bookbild.com");
	}

	displayLaunchButton(){
		if(this.state.user){
			return(
				LAUNCH
			);
		} else {
			return(
				LAUNCH
			);
		}
	}

	render(){
		// console.log('I am in header render');
		return (

						 SOLUTIONS
						 INVITE COMPANY
						 DEMO
						 INSIGHTS
						 TEAM
						{ /*  DEAL COMPARISON TOOL */}
						{this.displayLaunchButton()}

		    );
	}
}
<|endoftext|>


<commit_before>/**
 * Layout component that queries for data
 * with Gatsby's useStaticQuery component
 *
 * See: https://www.gatsbyjs.org/docs/use-static-query/
 */

import React from 'react'
import PropTypes from 'prop-types'
import { useStaticQuery, graphql } from 'gatsby'
import { Global, css } from '@emotion/core'
import styled from '@emotion/styled'
import Hero from './hero'
import Nav from './nav'
import NavLink, { NavAnchor } from './navlink'
import Banner from './banner'
import Footer from './footer'
import 'typeface-roboto'
import 'typeface-roboto-slab'

const globalStyles = css`
  * {
    box-sizing: border-box;
    text-size-adjust: none;
  }

  body {
    margin: 0;
    font-family: 'Roboto', 'sans-serif';
    font-size: 16px;
    line-height: 1.4;
    color: #444;
    background: #fbfbfb;
  }
`

const Container = styled.div`
  max-width: 1240px;
  margin: 0 auto;
  padding: 40px;
`

const Layout = ({ projectTitle, projectUrl, projectId, children }) => {
  const data = useStaticQuery(graphql`
    query SiteTitleQuery {
      site {
        siteMetadata {
          url
          title
          copyrightName
          shareText
          shareTextProjectStart
          shareTextProjectEnd
        }
      }
      allSiteMetadataMarkdownRemark {
        nodes {
          name
          html
        }
      }
    }
  `)

  const {
    url: siteUrl,
    shareText: defaultShareText,
    shareTextProjectStart,
    shareTextProjectEnd,
  } = data.site.siteMetadata
  const shareUrl = projectId ? `${siteUrl}/${projectId}` : siteUrl
  const shareText = projectId
    ? `${shareTextProjectStart}${projectTitle}${shareTextProjectEnd}`
    : defaultShareText
  const footerHtml = data.allSiteMetadataMarkdownRemark.nodes.find(({ name }) => {
    return name === 'footer'
  }).html

  return (
    <>
      <Global styles={globalStyles} />

      <Banner>
        Join us at the first Headless Commerce Summit, Sept 3rd. Learn more
      </Banner>

      <Hero siteTitle={data.site.siteMetadata.title} shareText={shareText} shareUrl={shareUrl} />

        <NavLink to="/about">About</NavLink>
        <NavLink to="/contribute">Contribute</NavLink>
        <NavAnchor href="https://jamstack.org" target="_blank" rel="noopener noreferrer">
          About Jamstack
        </NavAnchor>
        <NavAnchor href="https://headlesscms.org" target="_blank" rel="noopener noreferrer">
          Need a Static CMS?
        </NavAnchor>

      <Container>{children}</Container>

    </>
  )
}

Layout.propTypes = {
  children: PropTypes.node.isRequired,
}

export default Layout
<commit_msg>https://github.com/netlify/marketing/issues/1164<commit_after>/**
 * Layout component that queries for data
 * with Gatsby's useStaticQuery component
 *
 * See: https://www.gatsbyjs.org/docs/use-static-query/
 */

import React from 'react'
import PropTypes from 'prop-types'
import { useStaticQuery, graphql } from 'gatsby'
import { Global, css } from '@emotion/core'
import styled from '@emotion/styled'
import Hero from './hero'
import Nav from './nav'
import NavLink, { NavAnchor } from './navlink'
import Banner from './banner'
import Footer from './footer'
import 'typeface-roboto'
import 'typeface-roboto-slab'

const globalStyles = css`
  * {
    box-sizing: border-box;
    text-size-adjust: none;
  }

  body {
    margin: 0;
    font-family: 'Roboto', 'sans-serif';
    font-size: 16px;
    line-height: 1.4;
    color: #444;
    background: #fbfbfb;
  }
`

const Container = styled.div`
  max-width: 1240px;
  margin: 0 auto;
  padding: 40px;
`

const Layout = ({ projectTitle, projectUrl, projectId, children }) => {
  const data = useStaticQuery(graphql`
    query SiteTitleQuery {
      site {
        siteMetadata {
          url
          title
          copyrightName
          shareText
          shareTextProjectStart
          shareTextProjectEnd
        }
      }
      allSiteMetadataMarkdownRemark {
        nodes {
          name
          html
        }
      }
    }
  `)

  const {
    url: siteUrl,
    shareText: defaultShareText,
    shareTextProjectStart,
    shareTextProjectEnd,
  } = data.site.siteMetadata
  const shareUrl = projectId ? `${siteUrl}/${projectId}` : siteUrl
  const shareText = projectId
    ? `${shareTextProjectStart}${projectTitle}${shareTextProjectEnd}`
    : defaultShareText
  const footerHtml = data.allSiteMetadataMarkdownRemark.nodes.find(({ name }) => {
    return name === 'footer'
  }).html

  return (
    <>
      <Global styles={globalStyles} />

      <Banner>
        Join us at Jamstack Conf Virtual, October 6-7. Register now!
      </Banner>

      <Hero siteTitle={data.site.siteMetadata.title} shareText={shareText} shareUrl={shareUrl} />

        <NavLink to="/about">About</NavLink>
        <NavLink to="/contribute">Contribute</NavLink>
        <NavAnchor href="https://jamstack.org" target="_blank" rel="noopener noreferrer">
          About Jamstack
        </NavAnchor>
        <NavAnchor href="https://headlesscms.org" target="_blank" rel="noopener noreferrer">
          Need a Static CMS?
        </NavAnchor>

      <Container>{children}</Container>

    </>
  )
}

Layout.propTypes = {
  children: PropTypes.node.isRequired,
}

export default Layout
<|endoftext|>


<commit_before>var environ = require('./environ');

exports.baseLevelPath = require.resolve('bem/lib/levels/project');

exports.getTechs = function() {

    return require('bem').util.extend(this.__base() || {}, {
        'sets' : environ.getLibPath('bem-pr', 'bem/techs/sets.js')
    });

};
<commit_msg>Use level simple as prototype for project's root level<commit_after>var environ = require('./environ');

exports.baseLevelPath = require.resolve('bem/lib/levels/simple');

exports.getTechs = function() {

    return require('bem').util.extend(this.__base() || {}, {
        'sets' : environ.getLibPath('bem-pr', 'bem/techs/sets.js')
    });

};
<|endoftext|>


<commit_before>import React from 'react';
import TestItem from './test-item';

export default function(props) {
  return (

      {props.items.map(item => {
        return (
            <TestItem id={item.id} name={item.name} experience={item.experience}/>
        );
      })}

  );
}<commit_msg>added unique key to remove warning<commit_after>import React from 'react';
import TestItem from './test-item';

export default function(props) {
  return (

      {props.items.map(item => {
        return (
            <TestItem key={item.id} name={item.name} experience={item.experience}/>
        );
      })}

  );
}
<|endoftext|>


<commit_before>'use strict';

const path = require('path');
const fs = require('fs');
const fsp = fs.promises;
const sanitizePathname = require('../../utils/sanitizePathname');
const settings = require('../../utils/Settings');

exports.expressCreateServer = (hookName, args, cb) => {
  args.app.get('/tests/frontend/frontendTestSpecs.js', async (req, res) => {
    const [coreTests, pluginTests] = await Promise.all([getCoreTests(), getPluginTests()]);

    // merge the two sets of results
    let files = [].concat(coreTests, pluginTests).sort();

    // Keep only *.js files
    files = files.filter((f) => f.endsWith('.js'));

    // remove admin tests if the setting to enable them isn't in settings.json
    if (!settings.enableAdminUITests) {
      files = files.filter((file) => file.indexOf('admin') !== 0);
    }

    console.debug('Sent browser the following test specs:', files);
    res.setHeader('content-type', 'application/javascript');
    res.end(`window.frontendTestSpecs = ${JSON.stringify(files, null, 2)};\n`);
  });

  const rootTestFolder = path.join(settings.root, 'src/tests/frontend/');

  // The regexp /[\d\D]{0,}/ is equivalent to the regexp /.*/. The Express route path used here
  // uses the more verbose /[\d\D]{0,}/ pattern instead of /.*/ because path-to-regexp v0.1.7 (the
  // version used with Express v4.x) interprets '.' and '*' differently than regexp.
  args.app.get('/tests/frontend/:file([\\d\\D]{0,})', (req, res, next) => {
    (async () => {
      let relFile = sanitizePathname(req.params.file);
      if (['', '.', './'].includes(relFile)) relFile = 'index.html';
      const file = path.join(rootTestFolder, relFile);
      if (relFile.startsWith('specs/') && file.endsWith('.js')) {
        const content = await fsp.readFile(file);
        res.setHeader('content-type', 'application/javascript');
        res.send(`describe(${JSON.stringify(path.basename(file))}, function () {\n${content}\n});`);
      } else {
        res.sendFile(file);
      }
    })().catch((err) => next(err || new Error(err)));
  });

  args.app.get('/tests/frontend', (req, res) => {
    res.redirect('/tests/frontend/index.html');
  });

  return cb();
};

const getPluginTests = async (callback) => {
  const moduleDir = 'node_modules/';
  const specPath = '/static/tests/frontend/specs/';
  const staticDir = '/static/plugins/';

  const pluginSpecs = [];

  const plugins = await fsp.readdir(moduleDir);
  await Promise.all(plugins
      .map((plugin) => [plugin, moduleDir + plugin + specPath])
      .filter(([plugin, specDir]) => fs.existsSync(specDir)) // check plugin exists
      .map(async ([plugin, specDir]) => {
        const specFiles = await fsp.readdir(specDir);
        return specFiles.map((spec) => {
          pluginSpecs.push(staticDir + plugin + specPath + spec);
        });
      }));
  return pluginSpecs;
};

const getCoreTests = async () => await fsp.readdir('src/tests/frontend/specs');
<commit_msg>tests: Remove unnecessary `return`<commit_after>'use strict';

const path = require('path');
const fs = require('fs');
const fsp = fs.promises;
const sanitizePathname = require('../../utils/sanitizePathname');
const settings = require('../../utils/Settings');

exports.expressCreateServer = (hookName, args, cb) => {
  args.app.get('/tests/frontend/frontendTestSpecs.js', async (req, res) => {
    const [coreTests, pluginTests] = await Promise.all([getCoreTests(), getPluginTests()]);

    // merge the two sets of results
    let files = [].concat(coreTests, pluginTests).sort();

    // Keep only *.js files
    files = files.filter((f) => f.endsWith('.js'));

    // remove admin tests if the setting to enable them isn't in settings.json
    if (!settings.enableAdminUITests) {
      files = files.filter((file) => file.indexOf('admin') !== 0);
    }

    console.debug('Sent browser the following test specs:', files);
    res.setHeader('content-type', 'application/javascript');
    res.end(`window.frontendTestSpecs = ${JSON.stringify(files, null, 2)};\n`);
  });

  const rootTestFolder = path.join(settings.root, 'src/tests/frontend/');

  // The regexp /[\d\D]{0,}/ is equivalent to the regexp /.*/. The Express route path used here
  // uses the more verbose /[\d\D]{0,}/ pattern instead of /.*/ because path-to-regexp v0.1.7 (the
  // version used with Express v4.x) interprets '.' and '*' differently than regexp.
  args.app.get('/tests/frontend/:file([\\d\\D]{0,})', (req, res, next) => {
    (async () => {
      let relFile = sanitizePathname(req.params.file);
      if (['', '.', './'].includes(relFile)) relFile = 'index.html';
      const file = path.join(rootTestFolder, relFile);
      if (relFile.startsWith('specs/') && file.endsWith('.js')) {
        const content = await fsp.readFile(file);
        res.setHeader('content-type', 'application/javascript');
        res.send(`describe(${JSON.stringify(path.basename(file))}, function () {\n${content}\n});`);
      } else {
        res.sendFile(file);
      }
    })().catch((err) => next(err || new Error(err)));
  });

  args.app.get('/tests/frontend', (req, res) => {
    res.redirect('/tests/frontend/index.html');
  });

  return cb();
};

const getPluginTests = async (callback) => {
  const moduleDir = 'node_modules/';
  const specPath = '/static/tests/frontend/specs/';
  const staticDir = '/static/plugins/';

  const pluginSpecs = [];

  const plugins = await fsp.readdir(moduleDir);
  await Promise.all(plugins
      .map((plugin) => [plugin, moduleDir + plugin + specPath])
      .filter(([plugin, specDir]) => fs.existsSync(specDir)) // check plugin exists
      .map(async ([plugin, specDir]) => {
        const specFiles = await fsp.readdir(specDir);
        for (const spec of specFiles) {
          pluginSpecs.push(staticDir + plugin + specPath + spec);
        }
      }));
  return pluginSpecs;
};

const getCoreTests = async () => await fsp.readdir('src/tests/frontend/specs');
<|endoftext|>


<commit_before>import { Meteor } from 'meteor/meteor';
import { Template } from 'meteor/templating';
import { TAPi18n } from 'meteor/tap:i18n';
import { $ } from 'meteor/jquery';
import { Session } from 'meteor/session';
import { Router } from 'meteor/iron:router';

import { editorFadeOut } from '/imports/ui/templates/components/decision/editor/editor';
import { timers } from '/lib/const';
import { stripHTMLfromText } from '/imports/ui/modules/utils';
import { toggleSidebar } from '/imports/ui/modules/menu';

import './navigation.html';
import '../authentication/authentication.js';
import '../../widgets/notice/notice.js';

// Scroll behaviour
let lastScrollTop = 0;
let scrollDown = false;

function hideBar() {
  if (Meteor.Device.isPhone()) {
    $('.right').scroll(() => {
      const node = $('.navbar');
      const st = $('.right').scrollTop();
      if (st > lastScrollTop && st > 60) {
        scrollDown = true;
        node
          .velocity('stop')
          .velocity({ translateY: '0px' }, { duration: parseInt(timers.ANIMATION_DURATION, 10), easing: 'ease-out' })
          .velocity({ translateY: '-100px' }, {
            duration: parseInt(timers.ANIMATION_DURATION, 10),
            easing: 'ease-out',
            complete: () => {
              node.css('position', 'absolute');
              node.css('top', '0px');
            },
          })
          .velocity('stop');
      } else if (scrollDown === true) {
        scrollDown = false;
        node.css('position', 'fixed');
        node
          .velocity('stop')
          .velocity({ translateY: '-100px' }, { duration: parseInt(timers.ANIMATION_DURATION, 10), easing: 'ease-out' })
          .velocity({ translateY: '0px' }, {
            duration: parseInt(timers.ANIMATION_DURATION, 10),
            easing: 'ease-out',
            complete: () => {
            },
          })
          .velocity('stop');
      }
      lastScrollTop = st;
    });
  } else {
    $('.navbar').css('position', 'fixed');
  }
}

/**
* @summary verifies if current screen should have back button on navbar
*/
function displayBackButton() {
  return false;
  // return (Meteor.Device.isPhone() && (Router.current().url.search('/idea/') >= 0 || Router.current().url.search('/peer/') >= 0));
}

/**
* @summary verifies if editor mode is on in mobile devices
*/
function displayCancelButton() {
  return (Meteor.Device.isPhone() && Session.get('showPostEditor'));
}

function displayMenuIcon() {
  if (displayCancelButton()) {
    return 'images/cross.png';
  } else if (displayBackButton()) {
    return 'images/back.png';
  }
  if (Session.get('sidebar')) {
    return 'images/burger-active.png';
  }
  return 'images/burger.png';
}

Template.navigation.onRendered(() => {
  hideBar();
});

Template.navigation.helpers({
  screen() {
    if (Session.get('navbar')) {
      document.title = stripHTMLfromText(`${TAPi18n.__('democracy-of')} ${Meteor.settings.public.Collective.name} - ${Session.get('navbar').title}`);
      if (Session.get('navbar').title === Meteor.settings.public.Collective.name) {
        return '';
      }
      return Session.get('navbar').title;
    }
    document.title = stripHTMLfromText(TAPi18n.__('democracy-earth'));
    return '';
  },
  logo() {
    if (Session.get('navbar')) {
      return (Session.get('navbar').title === Meteor.settings.public.Collective.name);
    }
    return false;
  },
  icon() {
    if (Session.get('navbar')) {
      return displayMenuIcon();
    }
    return 'images/burger.png';
  },
  link() {
    if (Session.get('navbar')) {
      return Session.get('navbar').href;
    }
    return '';
  },
});

Template.navigation.events({
  'click #menu'() {
    if (displayCancelButton()) {
      editorFadeOut(Session.get('draftContract')._id);
      Session.set('showPostEditor', false);
    } else if (displayBackButton()) {
      window.history.back();
    } else { // if (Session.get('navbar').action === 'SIDEBAR')
      toggleSidebar();
    }
  },
});
<commit_msg>deprecate navbar session object<commit_after>import { Meteor } from 'meteor/meteor';
import { Template } from 'meteor/templating';
import { TAPi18n } from 'meteor/tap:i18n';
import { $ } from 'meteor/jquery';
import { Session } from 'meteor/session';
import { Router } from 'meteor/iron:router';

import { editorFadeOut } from '/imports/ui/templates/components/decision/editor/editor';
import { timers } from '/lib/const';
import { stripHTMLfromText } from '/imports/ui/modules/utils';
import { toggleSidebar } from '/imports/ui/modules/menu';
import { showFullName } from '/imports/startup/both/modules/utils';

import './navigation.html';
import '../authentication/authentication.js';
import '../../widgets/notice/notice.js';

// Scroll behaviour
let lastScrollTop = 0;
let scrollDown = false;

function hideBar() {
  if (Meteor.Device.isPhone()) {
    $('.right').scroll(() => {
      const node = $('.navbar');
      const st = $('.right').scrollTop();
      if (st > lastScrollTop && st > 60) {
        scrollDown = true;
        node
          .velocity('stop')
          .velocity({ translateY: '0px' }, { duration: parseInt(timers.ANIMATION_DURATION, 10), easing: 'ease-out' })
          .velocity({ translateY: '-100px' }, {
            duration: parseInt(timers.ANIMATION_DURATION, 10),
            easing: 'ease-out',
            complete: () => {
              node.css('position', 'absolute');
              node.css('top', '0px');
            },
          })
          .velocity('stop');
      } else if (scrollDown === true) {
        scrollDown = false;
        node.css('position', 'fixed');
        node
          .velocity('stop')
          .velocity({ translateY: '-100px' }, { duration: parseInt(timers.ANIMATION_DURATION, 10), easing: 'ease-out' })
          .velocity({ translateY: '0px' }, {
            duration: parseInt(timers.ANIMATION_DURATION, 10),
            easing: 'ease-out',
            complete: () => {
            },
          })
          .velocity('stop');
      }
      lastScrollTop = st;
    });
  } else {
    $('.navbar').css('position', 'fixed');
  }
}

/**
* @summary verifies if current screen should have back button on navbar
*/
function displayBackButton() {
  return false;
  // return (Meteor.Device.isPhone() && (Router.current().url.search('/idea/') >= 0 || Router.current().url.search('/peer/') >= 0));
}

/**
* @summary verifies if editor mode is on in mobile devices
*/
function displayCancelButton() {
  return (Meteor.Device.isPhone() && Session.get('showPostEditor'));
}

function displayMenuIcon() {
  if (displayCancelButton()) {
    return 'images/cross.png';
  } else if (displayBackButton()) {
    return 'images/back.png';
  }
  if (Session.get('sidebar')) {
    return 'images/burger-active.png';
  }
  return 'images/burger.png';
}

/**
* @summary verifies if user is currently at remove-option
*/
const _isRoot = () => {
  return (Router.current().url === '/' || Router.current().params.username === undefined);
};

Template.navigation.onRendered(() => {
  hideBar();
});

Template.navigation.helpers({
  screen() {
    if (Router.current().params.username) {
      const user = Meteor.users.findOne({ username: Router.current().params.username });
      return showFullName(user.profile.firstName, user.profile.lastName, user.username);
    }
    return '';
  },
  logo() {
    if (_isRoot()) {
      return true;
    }
    return false;
  },
  icon() {
    return displayMenuIcon();
  },
});

Template.navigation.events({
  'click #menu'() {
    if (displayCancelButton()) {
      editorFadeOut(Session.get('draftContract')._id);
      Session.set('showPostEditor', false);
    } else if (displayBackButton()) {
      window.history.back();
    } else {
      toggleSidebar();
    }
  },
});
<|endoftext|>


<commit_before>/// <reference path="Control.js"/>
/// <reference path="../Core/Collections/IListenCollectionChanged.js" />
/// CODE
/// <reference path="Panel.js"/>
/// <reference path="ItemsPresenter.js"/>
/// <reference path="../Collections/NotifyCollectionChangedEventArgs.js" />
/// <reference path="VirtualizingPanel.js"/>

//#region ItemsControl
var ItemsControl = Nullstone.Create("ItemsControl", Control, 0, [IListenCollectionChanged]);

ItemsControl.Instance.Init = function () {
    this.Init$Control();
    this.DefaultStyleKey = this.constructor;
    this._ItemContainerGenerator = new ItemContainerGenerator(this);
    this._ItemContainerGenerator.ItemsChanged.Subscribe(this.OnItemContainerGeneratorChanged, this);
};

//#region Properties

ItemsControl.DisplayMemberPathProperty = DependencyProperty.RegisterCore("DisplayMemberPath", function () { return String; }, ItemsControl, null, function (d, args) { d.OnDisplayMemberPathChanged(args); });
ItemsControl.ItemsProperty = DependencyProperty.RegisterCore("Items", function () { return ItemCollection; }, ItemsControl);
ItemsControl.ItemsPanelProperty = DependencyProperty.RegisterCore("ItemsPanel", function () { return ItemsPanelTemplate; }, ItemsControl);
ItemsControl.ItemsSourceProperty = DependencyProperty.RegisterCore("ItemsSource", function () { return Object; }, ItemsControl, null, function (d, args) { d.OnItemsSourceChanged(args); });
ItemsControl.ItemTemplateProperty = DependencyProperty.RegisterCore("ItemTemplate", function () { return DataTemplate; }, ItemsControl, undefined, function (d, args) { d.OnItemTemplateChanged(args); });

Nullstone.AutoProperties(ItemsControl, [
    ItemsControl.DisplayMemberPathProperty,
    ItemsControl.ItemsPanelProperty,
    ItemsControl.ItemTemplateProperty
]);

Nullstone.Property(ItemsControl, "Items", {
    get: function () {
        var items = Nullstone.As(this.$GetValue(ItemsControl.ItemsProperty), ItemCollection);
        if (items == null) {
            items = new ItemCollection();
            this._itemsIsDataBound = false;
            items.ItemsChanged.Subscribe(this.InvokeItemsChanged, this);
            items.Clearing.Subscribe(this.OnItemsClearing, this);
            this.$SetValue(ItemsControl.ItemsProperty, items);
        }
        return items;
    }
});

Nullstone.Property(ItemsControl, "ItemsSource", {
    get: function () { return this.$GetValue(ItemsControl.ItemsSourceProperty); },
    set: function (value) {
        if (!this._itemsIsDataBound && this.Items.GetCount() > 0)
            throw new InvalidOperationException("Items collection must be empty before using ItemsSource");
        this.$SetValue(ItemsControl.ItemsSourceProperty, value);
    }
});

Nullstone.Property(ItemsControl, "ItemContainerGenerator", {
    get: function () { return this._ItemContainerGenerator; }
});

// <DataTemplate><Grid><TextBlock Text="{Binding @DisplayMemberPath}" /></Grid></DataTemplate>
Nullstone.Property(ItemsControl, "$DisplayMemberTemplate", {
    get: function () {
        if (this._DisplayMemberTemplate == null) {
            var json = {
                Type: Grid,
                Children: [
                    {
                        Type: TextBlock,
                        Props: {
                            Text: new BindingMarkup({ Path: this.DisplayMemberPath })
                        }
                    }
                ]
            };
            this._DisplayMemberTemplate = new DataTemplate(json);
        }
        return this._DisplayMemberTemplate;
    }
});

Nullstone.Property(ItemsControl, "$Panel", {
    get: function () {
        return this._Presenter == null ? null : this._Presenter._ElementRoot;
    }
});

//#endregion

//#region Annotations

ItemsControl.Annotations = {
    ContentProperty: "Items"
};

//#endregion

ItemsControl.GetItemsOwner = function (element) {
    var panel = Nullstone.As(element, Panel);
    if (panel == null || !panel.IsItemsHost)
        return null;
    var owner = Nullstone.As(panel.TemplateOwner, ItemsPresenter);
    if (owner != null)
        return Nullstone.As(owner.TemplateOwner, ItemsControl);
    return null;
};
ItemsControl.ItemsControlFromItemContainer = function (container) {
    var e = Nullstone.As(container, FrameworkElement);
    if (e == null)
        return null;

    var itctl = Nullstone.As(e.Parent, ItemsControl);
    if (itctl == null)
        return ItemsControl.GetItemsOwner(e.Parent);
    if (itctl.IsItemItsOwnContainer(e))
        return itctl;
    return null;
};

ItemsControl.Instance._GetDefaultTemplate = function () {
    var presenter = this._Presenter;
    if (presenter == null) {
        presenter = new ItemsPresenter();
        presenter.TemplateOwner = this;
    }
    return presenter;
};
ItemsControl.Instance._SetItemsPresenter = function (presenter) {
    if (this._Presenter != null)
        this._Presenter._ElementRoot.Children.Clear();

    this._Presenter = presenter;
    this.AddItemsToPresenter(-1, 1, this.Items.GetCount());
};
ItemsControl.Instance.OnItemsSourceChanged = function (e) {
    if (!e.OldValue && Nullstone.Is(e.OldValue, INotifyCollectionChanged)) {
        e.OldValue.CollectionChanged.Unsubscribe(this._CollectionChanged, this);
    }

    if (e.NewValue != null) {
        if (Nullstone.Is(e.NewValue, INotifyCollectionChanged)) {
            e.NewValue.CollectionChanged.Subscribe(this._CollectionChanged, this);
        }

        this.Items._ReadOnly = true;
        this._itemsIsDataBound = true;
        this.Items._ClearImpl();

        var count = e.NewValue.length;
        for (var i = 0; i < count; i++) {
            this.Items._AddImpl(e.NewValue[i]);
        }

        this.OnItemsChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
    } else {
        this._itemsIsDataBound = false;
        this.Items._ReadOnly = false;
        this.Items._ClearImpl();
    }

    this._InvalidateMeasure();
};
ItemsControl.Instance._CollectionChanged = function (sender, e) {
    switch (e.Action) {
        case NotifyCollectionChangedAction.Add:
            var count = e.NewItems.GetCount();
            for (var i = 0; i < count; i++) {
                this.Items._InsertImpl(e.NewStartingIndex + 1, e.NewItems.GetValueAt(i));
            }
            break;
        case NotifyCollectionChangedAction.Remove:
            var count = e.OldItems.GetCount();
            for (var i = 0; i < count; i++) {
                this.Items._RemoveAtImpl(e.OldStartingIndex);
            }
            break;
        case NotifyCollectionChangedAction.Replace:
            var count = e.NewItems.GetCount();
            for (var i = 0; i < count; i++) {
                this.Items._SetItemImpl(e.NewStartingIndex + 1, e.NewItems.GetValueAt(i));
            }
            break;
        case NotifyCollectionChangedAction.Reset:
            this.Items._ClearImpl();
            var count = this.ItemsSource.GetCount();
            for (var i = 0; i < count; i++) {
                this.Items._AddImpl(this.ItemsSource.GetValueAt(i));
            }
            break;
    }
    this.OnItemsChanged(e);
};
ItemsControl.Instance.OnDisplayMemberPathChanged = function (e) {
    var items = this.Items;
    var count = items.GetCount();
    for (var i = 0; i < count; i++) {
        this.UpdateContentTemplateOnContainer(ItemContainerGenerator.ContainerFromIndex(i), items.GetValueAt(i));
    }
};
ItemsControl.Instance.ClearContainerForItem = function (element, item) { };
ItemsControl.Instance.GetContainerForItem = function () {
    return new ContentPresenter();
};
ItemsControl.Instance.IsItemItsOwnContainer = function (item) {
    return item instanceof FrameworkElement;
};
ItemsControl.Instance.OnItemsChanged = function (e) { };
ItemsControl.Instance.OnItemsClearing = function (object, e) {
    this.SetLogicalParent(null, this.Items.ToArray());
};
ItemsControl.Instance.InvokeItemsChanged = function (object, e) {
    switch (e.Action) {
        case NotifyCollectionChangedAction.Add:
            this.SetLogicalParent(this, e.NewItems);
            break;
        case NotifyCollectionChangedAction.Remove:
            this.SetLogicalParent(null, e.OldItems);
            break;
        case NotifyCollectionChangedAction.Replace:
            this.SetLogicalParent(null, e.OldItems);
            this.SetLogicalParent(this, e.NewItems);
            break;
    }

    this._ItemContainerGenerator.OnOwnerItemsItemsChanged(object, e);
    if (!this._itemsIsDataBound)
        this.OnItemsChanged(e);
};
ItemsControl.Instance.OnItemContainerGeneratorChanged = function (sender, e) {
    if (this._Presenter == null || this._Presenter._ElementRoot instanceof VirtualizingPanel)
        return;

    var panel = this._Presenter._ElementRoot;
    switch (e.GetAction()) {
        case NotifyCollectionChangedAction.Reset:
            var count = panel.Children.GetCount();
            if (count > 0)
                this.RemoveItemsFromPresenter(0, 0, count);
            break;
        case NotifyCollectionChangedAction.Add:
            this.AddItemsToPresenter(e.PositionIndex, e.PositionOffset, e.ItemCount);
            break;
        case NotifyCollectionChangedAction.Remove:
            this.RemoveItemsFromPresenter(e.PositionIndex, e.PositionOffset, e.ItemCount);
            break;
        case NotifyCollectionChangedAction.Replace:
            this.RemoveItemsFromPresenter(e.PositionIndex, e.PositionOffset, e.ItemCount);
            this.AddItemsToPresenter(e.Position, e.ItemCount);
            break;
    }
};
ItemsControl.Instance.OnItemTemplateChanged = function (e) {
    var items = this.Items;
    var count = items.GetCount();
    for (var i = 0; i < count; i++) {
        this.UpdateContentTemplateOnContainer(this.ItemContainerGenerator.ContainerFromIndex(i), items.GetValueAt(i));
    }
};
ItemsControl.Instance.SetLogicalParent = function (parent, items) {
    if (this.ItemsSource != null)
        return;

    var error = new BError();
    var count = items.length;
    for (var i = 0; i < count; i++) {
        var fe = Nullstone.As(items[i], FrameworkElement);
        if (fe == null)
            continue;
        this._SetLogicalParent(parent, error);
        if (error.IsErrored())
            throw error.CreateException();
    }
};
ItemsControl.Instance.AddItemsToPresenter = function (positionIndex, positionOffset, count) {
    if (this._Presenter == null || this._Presenter._ElementRoot == null || this._Presenter._ElementRoot instanceof VirtualizingPanel)
        return;

    var panel = this._Presenter._ElementRoot;
    var newIndex = this._ItemContainerGenerator.IndexFromGeneratorPosition(positionIndex, positionOffset);
    var p = this._ItemContainerGenerator.StartAt(positionIndex, positionOffset, 0, true);
    var items = this.Items;
    var children = panel.Children;
    for (var i = 0; i < count; i++) {
        var item = items.GetValueAt(newIndex + 1);
        var container = this._ItemContainerGenerator.GenerateNext({});
        if (container instanceof ContentControl)
            container._ContentSetsParent = false;

        if (container instanceof FrameworkElement && !(item instanceof FrameworkElement))
            container.DataContext = item;

        children.Insert(newIndex + i, container);
        this._ItemContainerGenerator.PrepareItemContainer(container);
    }
};
ItemsControl.Instance.RemoveItemsFromPresenter = function (positionIndex, positionOffset, count) {
    if (this._Presenter == null || this._Presenter._ElementRoot == null || this._Presenter._ElementRoot instanceof VirtualizingPanel)
        return;

    var panel = this._Presenter._ElementRoot;
    while (count > 0) {
        panel.Children.RemoveAt(positionIndex);
        count--;
    }
};
ItemsControl.Instance.PrepareContainerForItem = function (element, item) {
    if (this.DisplayMemberPath != null && this.ItemTemplate != null)
        throw new InvalidOperationException("Cannot set 'DisplayMemberPath' and 'ItemTemplate' simultaenously");

    this.UpdateContentTemplateOnContainer(element, item);
};
ItemsControl.Instance.UpdateContentTemplateOnContainer = function (element, item) {
    if (Nullstone.RefEquals(element, item))
        return;

    var presenter = Nullstone.As(element, ContentPresenter);
    var control = Nullstone.As(element, ContentControl);

    var template;
    if (!(item instanceof UIElement)) {
        template = this.ItemTemplate;
        if (template == null)
            template = this.$DisplayMemberTemplate;
    }

    if (presenter != null) {
        presenter.ContentTemplate = template;
        presenter.Content = item;
    } else if (control != null) {
        control.ContentTemplate = template;
        control.Content = item;
    }
};

Nullstone.FinishCreate(ItemsControl);
//#endregion<commit_msg>Fixed references to ItemContainerGenerator identifiers.<commit_after>/// <reference path="Control.js"/>
/// <reference path="../Core/Collections/IListenCollectionChanged.js" />
/// CODE
/// <reference path="Panel.js"/>
/// <reference path="ItemsPresenter.js"/>
/// <reference path="../Collections/NotifyCollectionChangedEventArgs.js" />
/// <reference path="VirtualizingPanel.js"/>

//#region ItemsControl
var ItemsControl = Nullstone.Create("ItemsControl", Control, 0, [IListenCollectionChanged]);

ItemsControl.Instance.Init = function () {
    this.Init$Control();
    this.DefaultStyleKey = this.constructor;
    this._ItemContainerGenerator = new ItemContainerGenerator(this);
    this._ItemContainerGenerator.ItemsChanged.Subscribe(this.OnItemContainerGeneratorChanged, this);
};

//#region Properties

ItemsControl.DisplayMemberPathProperty = DependencyProperty.RegisterCore("DisplayMemberPath", function () { return String; }, ItemsControl, null, function (d, args) { d.OnDisplayMemberPathChanged(args); });
ItemsControl.ItemsProperty = DependencyProperty.RegisterCore("Items", function () { return ItemCollection; }, ItemsControl);
ItemsControl.ItemsPanelProperty = DependencyProperty.RegisterCore("ItemsPanel", function () { return ItemsPanelTemplate; }, ItemsControl);
ItemsControl.ItemsSourceProperty = DependencyProperty.RegisterCore("ItemsSource", function () { return Object; }, ItemsControl, null, function (d, args) { d.OnItemsSourceChanged(args); });
ItemsControl.ItemTemplateProperty = DependencyProperty.RegisterCore("ItemTemplate", function () { return DataTemplate; }, ItemsControl, undefined, function (d, args) { d.OnItemTemplateChanged(args); });

Nullstone.AutoProperties(ItemsControl, [
    ItemsControl.DisplayMemberPathProperty,
    ItemsControl.ItemsPanelProperty,
    ItemsControl.ItemTemplateProperty
]);

Nullstone.Property(ItemsControl, "Items", {
    get: function () {
        var items = Nullstone.As(this.$GetValue(ItemsControl.ItemsProperty), ItemCollection);
        if (items == null) {
            items = new ItemCollection();
            this._itemsIsDataBound = false;
            items.ItemsChanged.Subscribe(this.InvokeItemsChanged, this);
            items.Clearing.Subscribe(this.OnItemsClearing, this);
            this.$SetValue(ItemsControl.ItemsProperty, items);
        }
        return items;
    }
});

Nullstone.Property(ItemsControl, "ItemsSource", {
    get: function () { return this.$GetValue(ItemsControl.ItemsSourceProperty); },
    set: function (value) {
        if (!this._itemsIsDataBound && this.Items.GetCount() > 0)
            throw new InvalidOperationException("Items collection must be empty before using ItemsSource");
        this.$SetValue(ItemsControl.ItemsSourceProperty, value);
    }
});

Nullstone.Property(ItemsControl, "ItemContainerGenerator", {
    get: function () { return this._ItemContainerGenerator; }
});

// <DataTemplate><Grid><TextBlock Text="{Binding @DisplayMemberPath}" /></Grid></DataTemplate>
Nullstone.Property(ItemsControl, "$DisplayMemberTemplate", {
    get: function () {
        if (this._DisplayMemberTemplate == null) {
            var json = {
                Type: Grid,
                Children: [
                    {
                        Type: TextBlock,
                        Props: {
                            Text: new BindingMarkup({ Path: this.DisplayMemberPath })
                        }
                    }
                ]
            };
            this._DisplayMemberTemplate = new DataTemplate(json);
        }
        return this._DisplayMemberTemplate;
    }
});

Nullstone.Property(ItemsControl, "$Panel", {
    get: function () {
        return this._Presenter == null ? null : this._Presenter._ElementRoot;
    }
});

//#endregion

//#region Annotations

ItemsControl.Annotations = {
    ContentProperty: "Items"
};

//#endregion

ItemsControl.GetItemsOwner = function (element) {
    var panel = Nullstone.As(element, Panel);
    if (panel == null || !panel.IsItemsHost)
        return null;
    var owner = Nullstone.As(panel.TemplateOwner, ItemsPresenter);
    if (owner != null)
        return Nullstone.As(owner.TemplateOwner, ItemsControl);
    return null;
};
ItemsControl.ItemsControlFromItemContainer = function (container) {
    var e = Nullstone.As(container, FrameworkElement);
    if (e == null)
        return null;

    var itctl = Nullstone.As(e.Parent, ItemsControl);
    if (itctl == null)
        return ItemsControl.GetItemsOwner(e.Parent);
    if (itctl.IsItemItsOwnContainer(e))
        return itctl;
    return null;
};

ItemsControl.Instance._GetDefaultTemplate = function () {
    var presenter = this._Presenter;
    if (presenter == null) {
        presenter = new ItemsPresenter();
        presenter.TemplateOwner = this;
    }
    return presenter;
};
ItemsControl.Instance._SetItemsPresenter = function (presenter) {
    if (this._Presenter != null)
        this._Presenter._ElementRoot.Children.Clear();

    this._Presenter = presenter;
    this.AddItemsToPresenter(-1, 1, this.Items.GetCount());
};
ItemsControl.Instance.OnItemsSourceChanged = function (e) {
    if (!e.OldValue && Nullstone.Is(e.OldValue, INotifyCollectionChanged)) {
        e.OldValue.CollectionChanged.Unsubscribe(this._CollectionChanged, this);
    }

    if (e.NewValue != null) {
        if (Nullstone.Is(e.NewValue, INotifyCollectionChanged)) {
            e.NewValue.CollectionChanged.Subscribe(this._CollectionChanged, this);
        }

        this.Items._ReadOnly = true;
        this._itemsIsDataBound = true;
        this.Items._ClearImpl();

        var count = e.NewValue.length;
        for (var i = 0; i < count; i++) {
            this.Items._AddImpl(e.NewValue[i]);
        }

        this.OnItemsChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
    } else {
        this._itemsIsDataBound = false;
        this.Items._ReadOnly = false;
        this.Items._ClearImpl();
    }

    this._InvalidateMeasure();
};
ItemsControl.Instance._CollectionChanged = function (sender, e) {
    switch (e.Action) {
        case NotifyCollectionChangedAction.Add:
            var count = e.NewItems.GetCount();
            for (var i = 0; i < count; i++) {
                this.Items._InsertImpl(e.NewStartingIndex + 1, e.NewItems.GetValueAt(i));
            }
            break;
        case NotifyCollectionChangedAction.Remove:
            var count = e.OldItems.GetCount();
            for (var i = 0; i < count; i++) {
                this.Items._RemoveAtImpl(e.OldStartingIndex);
            }
            break;
        case NotifyCollectionChangedAction.Replace:
            var count = e.NewItems.GetCount();
            for (var i = 0; i < count; i++) {
                this.Items._SetItemImpl(e.NewStartingIndex + 1, e.NewItems.GetValueAt(i));
            }
            break;
        case NotifyCollectionChangedAction.Reset:
            this.Items._ClearImpl();
            var count = this.ItemsSource.GetCount();
            for (var i = 0; i < count; i++) {
                this.Items._AddImpl(this.ItemsSource.GetValueAt(i));
            }
            break;
    }
    this.OnItemsChanged(e);
};
ItemsControl.Instance.OnDisplayMemberPathChanged = function (e) {
    var items = this.Items;
    var count = items.GetCount();
    for (var i = 0; i < count; i++) {
        this.UpdateContentTemplateOnContainer(ItemContainerGenerator.ContainerFromIndex(i), items.GetValueAt(i));
    }
};
ItemsControl.Instance.ClearContainerForItem = function (element, item) { };
ItemsControl.Instance.GetContainerForItem = function () {
    return new ContentPresenter();
};
ItemsControl.Instance.IsItemItsOwnContainer = function (item) {
    return item instanceof FrameworkElement;
};
ItemsControl.Instance.OnItemsChanged = function (e) { };
ItemsControl.Instance.OnItemsClearing = function (object, e) {
    this.SetLogicalParent(null, this.Items.ToArray());
};
ItemsControl.Instance.InvokeItemsChanged = function (object, e) {
    switch (e.Action) {
        case NotifyCollectionChangedAction.Add:
            this.SetLogicalParent(this, e.NewItems);
            break;
        case NotifyCollectionChangedAction.Remove:
            this.SetLogicalParent(null, e.OldItems);
            break;
        case NotifyCollectionChangedAction.Replace:
            this.SetLogicalParent(null, e.OldItems);
            this.SetLogicalParent(this, e.NewItems);
            break;
    }

    this._ItemContainerGenerator.OnOwnerItemsItemsChanged(object, e);
    if (!this._itemsIsDataBound)
        this.OnItemsChanged(e);
};
ItemsControl.Instance.OnItemContainerGeneratorChanged = function (sender, e) {
    if (this._Presenter == null || this._Presenter._ElementRoot instanceof VirtualizingPanel)
        return;

    var panel = this._Presenter._ElementRoot;
    switch (e.Action) {
        case NotifyCollectionChangedAction.Reset:
            var count = panel.Children.GetCount();
            if (count > 0)
                this.RemoveItemsFromPresenter(0, 0, count);
            break;
        case NotifyCollectionChangedAction.Add:
            this.AddItemsToPresenter(e.Position.index, e.Position.offset, e.ItemCount);
            break;
        case NotifyCollectionChangedAction.Remove:
            this.RemoveItemsFromPresenter(e.Position.index, e.Position.offset, e.ItemCount);
            break;
        case NotifyCollectionChangedAction.Replace:
            this.RemoveItemsFromPresenter(e.Position.index, e.Position.offset, e.ItemCount);
            this.AddItemsToPresenter(e.Position.index, e.Position.offset, e.ItemCount);
            break;
    }
};
ItemsControl.Instance.OnItemTemplateChanged = function (e) {
    var items = this.Items;
    var count = items.GetCount();
    for (var i = 0; i < count; i++) {
        this.UpdateContentTemplateOnContainer(this.ItemContainerGenerator.ContainerFromIndex(i), items.GetValueAt(i));
    }
};
ItemsControl.Instance.SetLogicalParent = function (parent, items) {
    if (this.ItemsSource != null)
        return;

    var error = new BError();
    var count = items.length;
    for (var i = 0; i < count; i++) {
        var fe = Nullstone.As(items[i], FrameworkElement);
        if (fe == null)
            continue;
        this._SetLogicalParent(parent, error);
        if (error.IsErrored())
            throw error.CreateException();
    }
};
ItemsControl.Instance.AddItemsToPresenter = function (positionIndex, positionOffset, count) {
    if (this._Presenter == null || this._Presenter._ElementRoot == null || this._Presenter._ElementRoot instanceof VirtualizingPanel)
        return;

    var panel = this._Presenter._ElementRoot;
    var newIndex = this._ItemContainerGenerator.IndexFromGeneratorPosition(positionIndex, positionOffset);
    var p = this._ItemContainerGenerator.StartAt(positionIndex, positionOffset, 0, true);
    var items = this.Items;
    var children = panel.Children;
    for (var i = 0; i < count; i++) {
        var item = items.GetValueAt(newIndex + 1);
        var container = this._ItemContainerGenerator.GenerateNext({});
        if (container instanceof ContentControl)
            container._ContentSetsParent = false;

        if (container instanceof FrameworkElement && !(item instanceof FrameworkElement))
            container.DataContext = item;

        children.Insert(newIndex + i, container);
        this._ItemContainerGenerator.PrepareItemContainer(container);
    }
    delete this._ItemContainerGenerator._GenerationState;
};
ItemsControl.Instance.RemoveItemsFromPresenter = function (positionIndex, positionOffset, count) {
    if (this._Presenter == null || this._Presenter._ElementRoot == null || this._Presenter._ElementRoot instanceof VirtualizingPanel)
        return;

    var panel = this._Presenter._ElementRoot;
    while (count > 0) {
        panel.Children.RemoveAt(positionIndex);
        count--;
    }
};
ItemsControl.Instance.PrepareContainerForItem = function (element, item) {
    if (this.DisplayMemberPath != null && this.ItemTemplate != null)
        throw new InvalidOperationException("Cannot set 'DisplayMemberPath' and 'ItemTemplate' simultaenously");

    this.UpdateContentTemplateOnContainer(element, item);
};
ItemsControl.Instance.UpdateContentTemplateOnContainer = function (element, item) {
    if (Nullstone.RefEquals(element, item))
        return;

    var presenter = Nullstone.As(element, ContentPresenter);
    var control = Nullstone.As(element, ContentControl);

    var template;
    if (!(item instanceof UIElement)) {
        template = this.ItemTemplate;
        if (template == null)
            template = this.$DisplayMemberTemplate;
    }

    if (presenter != null) {
        presenter.ContentTemplate = template;
        presenter.Content = item;
    } else if (control != null) {
        control.ContentTemplate = template;
        control.Content = item;
    }
};

Nullstone.FinishCreate(ItemsControl);
//#endregion<|endoftext|>


<commit_before>(function (env) {
    "use strict";
    env.ddg_spice_islamic_prayer_times = function(api_result) {

        if (!api_result || api_result.error || api_result.status_code === 0 || !api_result.items) {
            return Spice.failed('islamic_prayer_times');
        }

        DDG.require('moment.js', function() {

            function capitalize(string) {
                return string.charAt(0).toUpperCase() + string.slice(1);
            }

            function getClosest(timings, index) {
                var today = moment();
                for (var property in timings) {
                    if (timings.hasOwnProperty(property)) {
                        var time = moment(timings[property], 'hh:mm A').add('days', index);
                        if (today.diff(time) < 0) {
                            var min = {
                                diff: time.from(today),
                                title: capitalize(property)
                            };
                            return min;
                        }
                    }
                }
                return false;
            }

            function getInfoboxData(timings) {
                var infoboxData = [];
                if ('date_for' in timings) delete timings.date_for; // remove the unnecessary one
                for (var property in timings) {
                    if (timings.hasOwnProperty(property)) {
                        infoboxData.push({
                            label: capitalize(property),
                            value: timings[property]
                        });
                    }
                }
                return infoboxData;
            }

            Spice.add({
                id: "islamic_prayer_times",
                name: "Islamic Prayer Times",
                data: api_result,
                meta: {
                    sourceName: "Muslim Salat",
                    sourceUrl: api_result.link
                },
                normalize: function(data) {
                    console.log(data);
                    // if isha, which is the last prayer in the day, is past, then get results for tomorrow
                    var index = moment().diff(moment(data.items[0].isha, 'hh:mm A')) > 0 ? 1 : 0;
                    return {
                        title: data.title,
                        datum: moment(data.items[index].date_for, 'YYYY-MM-DD').format('LL'),
                        infoboxData: getInfoboxData(data.items[index]),
                        closest: getClosest(data.items[index], index),
                        qibla: data.qibla_direction
                    };
                },
                templates: {
                    group: 'info',
                    options: {
                        content: Spice.islamic_prayer_times.islamic_prayer_times,
                        moreAt: false
                    }
                }
            });

        });
    };
}(this));
<commit_msg>IslamicPrayerTimes: Local time fixes<commit_after>(function (env) {
    "use strict";
    env.ddg_spice_islamic_prayer_times = function(api_result) {

        if (!api_result || api_result.error || api_result.status_code === 0 || !api_result.items) {
            return Spice.failed('islamic_prayer_times');
        }

        DDG.require('moment.js', function() {

            function capitalize(string) {
                return string.charAt(0).toUpperCase() + string.slice(1);
            }

            function setDst(time, dst) {
                return dst === 1 ? time.add(dst, 'hours') : time;
            }

            function tz(time, offset) {
                var clone = time.clone();
                clone.utcOffset(offset);
                clone.add(time.utcOffset() - clone.utcOffset(), 'minutes');
                return clone;
            }

            function getClosest(timings, offset, local) {
                var date = local.format('YYYY-MM-DD');
                if ('date_for' in timings) {
                    date = tz(moment.utc(timings.date_for + ' ' + offset, 'YYYY-MM-DD Z'), offset).format('YYYY-MM-DD');
                    delete timings.date_for;
                }
                for (var property in timings) {
                    if (timings.hasOwnProperty(property)) {
                        var time = tz(moment.utc(date + ' ' + timings[property] + ' ' + offset, 'YYYY-MM-DD hh:mm a Z'), offset);
                        if (local.diff(time) < 0) {
                            var min = {
                                diff: time.from(local),
                                title: capitalize(property)
                            };
                            return min;
                        }
                    }
                }
                return false;
            }

            function getInfoboxData(timings) {
                var infoboxData = [];
                for (var property in timings) {
                    if (timings.hasOwnProperty(property) && property !== "date_for") {
                        infoboxData.push({
                            label: capitalize(property),
                            value: timings[property]
                        });
                    }
                }
                return infoboxData;
            }

            Spice.add({
                id: "islamic_prayer_times",
                name: "Islamic Prayer Times",
                data: api_result,
                meta: {
                    sourceName: "Muslim Salat",
                    sourceUrl: api_result.link
                },
                normalize: function(data) {
                    var offset = parseInt(data.timezone),
                        dst    = parseInt(data.daylight),
                        local  = setDst(moment().utcOffset(offset), dst),
                        isha   = tz(moment.utc(data.items[0].date_for + ' ' + data.items[0].isha + ' ' + offset, 'YYYY-MM-DD hh:mm a Z'), offset),
                        index  = local.diff(isha) > 0 ? 1 : 0; // if isha, which is the last prayer in the day, is past, then get the results for tomorrow
                    return {
                        title: data.title,
                        datum: local.format('LLLL'),
                        infoboxData: getInfoboxData(data.items[index]),
                        closest: getClosest(data.items[index], offset, local),
                        qibla: data.qibla_direction
                    };
                },
                templates: {
                    group: 'info',
                    options: {
                        content: Spice.islamic_prayer_times.islamic_prayer_times,
                        moreAt: false
                    }
                }
            });

        });
    };
}(this));
<|endoftext|>