<commit_before>var pymChild = null;

var colors = {
    'brown': '#6b6256','tan': '#a5a585','ltgreen': '#70a99a','green': '#449970','dkgreen': '#31716e','ltblue': '#55b7d9','blue': '#358fb3','dkblue': '#006c8e','yellow': '#f1bb4f','orange': '#f6883e','tangerine': '#e8604d','red': '#cc203b','pink': '#c72068','maroon': '#8c1b52','purple': '#571751'
};

/*
 * Render the graphic
 */
function render(width) {

		var northEast = L.latLng(38.778912, -90.151062),
		    southWest = L.latLng(38.556959, -90.461426),
		    bounds = L.latLngBounds(southWest, northEast);

		var map = L.map('map', {scrollWheelZoom: false, attribution: ''}).setView([38.673945, -90.273416], 11).setMaxBounds(bounds);

		var tiles = L.tileLayer('https://{s}.tiles.mapbox.com/v3/stlpr.hi06d4b5/{z}/{x}/{y}.png', { attribution: "© Mapbox © OpenStreetMap Improve this map"})
		    .addTo(map);

	    var svg = d3.select(map.getPanes().overlayPane).append("svg"),
	        g = svg.append("g").attr("class", "leaflet-zoom-hide");

			// update filename, collection name

			 d3.json("data.topojson", function(error, geodata) {

				 var regions = topojson.feature(geodata, geodata.objects.collection);
				 var geojson;

				 //set up styling logic

				 function getColor(d) {

			 //example logic to get color based on number
			 return d < .95 ? colors.red :
			 		d < .96 ? colors.tangerine :
			 		d < .97 ? colors.orange :
				 	d < .98 ? colors.yellow :
			 		d < .99 ? colors.green :
				 	colors.dkgreen;

					 // example logic to get color based on string
					 // (d == 'dropped' ? colors.green :
					 //  d == 'new' ? colors.red :
					 //  colors.maroon)

					 }

				 function style(feature) {
						 return {
							 // update property
					         fillColor: getColor(feature.properties.property),
					         weight: 1,
					         opacity: 1,
					         color: colors.yellow,
					         dashArray: '3',
					         fillOpacity: 0.7
					     };
					 }

				 //set up highlighting function

				 function highlightFeature(e) {
				     var layer = e.target;

				     layer.setStyle({
				         weight: 5,
				         color: '#666',
				         dashArray: '',
				         fillOpacity: 0.7
				     });

					 info.update(layer.feature.properties);

				     if (!L.Browser.ie && !L.Browser.opera) {
				         layer.bringToFront();
				     }
				 }

				 function resetHighlight(e) {
				     geojson.resetStyle(e.target);
					 info.update();
				 }

				 function onEachFeature(feature, layer) {
				     layer.on({
				         mouseover: highlightFeature,
				         mouseout: resetHighlight
				     });
				 }

				 geojson = L.geoJson(regions, {
					 style: style,
					 onEachFeature: onEachFeature
				 }).addTo(map);

				 // add infobox

				 var info = L.control();

				 info.onAdd = function (map) {
				     this._div = L.DomUtil.create('div', 'info'); // create a div with a class "info"
				     this.update();
				     return this._div;
				 };

				 // method that we will use to update the control based on feature properties passed
				 info.update = function (props) {
				     this._div.innerHTML = '
Infobox Title
Infobox description.

'

					 // example ternary logic for infobox
					 // +  (
// 						 props ?
// 						 props.STATUS == 'new' ?
// 						 "" + props.MUNICIPALI + ' was added to the suit.'
// 						 : props.STATUS == 'old' ?
// 						 "" + props.MUNICIPALI + ' is another municipality named in the suit.'
// 						 : "" + props.MUNICIPALI + ' has been dropped from the suit.'
// 						 : 'Hover over a county');
				 };

				 info.addTo(map);

				 // add legend

				 var legend = L.control({position: 'bottomright'});

				 legend.onAdd = function (map) {

				     var div = L.DomUtil.create('div', 'info legend')

					 // Legend with strings
					 // div.innerHTML = ' Added to the suit
 Also named in the suit
 Dropped from the suit
';

					 // Legend with numbers
					 grades = [.94, .95, .96, .97, .98, .99],
					 text = ['< 95%','95-96%','96-97%','97-98%','98-99%','>99%']
					 labels = [];
					 for (var i = 0; i < grades.length; i++) {
						 	from = grades[i];
							to = grades[i + 1];
							labels.push(
								' ' +
								text[i] );
						}
						div.innerHTML = labels.join('
');

				     return div;
				 };

				 legend.addTo(map);

		     })

    if (pymChild) {
        pymChild.sendHeight();
    }
}

/*
 * NB: Use window.load instead of document.ready
 * to ensure all images have loaded
 */
$(window).load(function() {
    pymChild = new pym.Child({
        renderCallback: render
    });
})
<commit_msg>update leaflet map for touch<commit_after>var pymChild = null;

var colors = {
    'brown': '#6b6256','tan': '#a5a585','ltgreen': '#70a99a','green': '#449970','dkgreen': '#31716e','ltblue': '#55b7d9','blue': '#358fb3','dkblue': '#006c8e','yellow': '#f1bb4f','orange': '#f6883e','tangerine': '#e8604d','red': '#cc203b','pink': '#c72068','maroon': '#8c1b52','purple': '#571751'
};

/*
 * Render the graphic
 */
function render(width) {

		var northEast = L.latLng(38.778912, -90.151062),
		    southWest = L.latLng(38.556959, -90.461426),
		    bounds = L.latLngBounds(southWest, northEast);

		var map = L.map('map', {scrollWheelZoom: false, attribution: ''}).setView([38.673945, -90.273416], 11).setMaxBounds(bounds);

		var tiles = L.tileLayer('https://{s}.tiles.mapbox.com/v3/stlpr.hi06d4b5/{z}/{x}/{y}.png', { attribution: "© Mapbox © OpenStreetMap Improve this map"})
		    .addTo(map);

	    var svg = d3.select(map.getPanes().overlayPane).append("svg"),
	        g = svg.append("g").attr("class", "leaflet-zoom-hide");

			// update filename, collection name

			 d3.json("data.topojson", function(error, geodata) {

				 var regions = topojson.feature(geodata, geodata.objects.collection);
				 var geojson;

				 //set up styling logic

				 function getColor(d) {

			 //example logic to get color based on number
			 return d < .95 ? colors.red :
			 		d < .96 ? colors.tangerine :
			 		d < .97 ? colors.orange :
				 	d < .98 ? colors.yellow :
			 		d < .99 ? colors.green :
				 	colors.dkgreen;

					 // example logic to get color based on string
					 // (d == 'dropped' ? colors.green :
					 //  d == 'new' ? colors.red :
					 //  colors.maroon)

					 }

				 function style(feature) {
						 return {
							 // update property
					         fillColor: getColor(feature.properties.property),
					         weight: 1,
					         opacity: 1,
					         color: colors.yellow,
					         dashArray: '3',
					         fillOpacity: 0.7
					     };
					 }

				 //set up highlighting function

				 function highlightFeature(e) {
				     var layer = e.target;

				     layer.setStyle({
				         weight: 5,
				         color: '#666',
				         dashArray: '',
				         fillOpacity: 0.7
				     });

					 info.update(layer.feature.properties);

				     if (!L.Browser.ie && !L.Browser.opera) {
				         layer.bringToFront();
				     }
				 }

				 function resetHighlight(e) {
				     geojson.resetStyle(e.target);
					 info.update();
				 }

                 function clickHighlight(e) {
                     geojson.eachLayer( function (layer) {
                         geojson.resetStyle(layer);
                     });
                     highlightFeature(e)
                 }

				 function onEachFeature(feature, layer) {
				     layer.on({
                         mouseover: highlightFeature,
                         mouseout: resetHighlight,
                         click: clickHighlight
				     });
				 }

				 geojson = L.geoJson(regions, {
					 style: style,
					 onEachFeature: onEachFeature
				 }).addTo(map);

				 // add infobox

				 var info = L.control();

				 info.onAdd = function (map) {
				     this._div = L.DomUtil.create('div', 'info'); // create a div with a class "info"
				     this.update();
				     return this._div;
				 };

				 // method that we will use to update the control based on feature properties passed
				 info.update = function (props) {
				     this._div.innerHTML = '
Infobox Title
Infobox description.

'

					 // example ternary logic for infobox
					 // +  (
// 						 props ?
// 						 props.STATUS == 'new' ?
// 						 "" + props.MUNICIPALI + ' was added to the suit.'
// 						 : props.STATUS == 'old' ?
// 						 "" + props.MUNICIPALI + ' is another municipality named in the suit.'
// 						 : "" + props.MUNICIPALI + ' has been dropped from the suit.'
// 						 : 'Hover over a county');
				 };

				 info.addTo(map);

				 // add legend

				 var legend = L.control({position: 'bottomright'});

				 legend.onAdd = function (map) {

				     var div = L.DomUtil.create('div', 'info legend')

					 // Legend with strings
					 // div.innerHTML = ' Added to the suit
 Also named in the suit
 Dropped from the suit
';

					 // Legend with numbers
					 grades = [.94, .95, .96, .97, .98, .99],
					 text = ['< 95%','95-96%','96-97%','97-98%','98-99%','>99%']
					 labels = [];
					 for (var i = 0; i < grades.length; i++) {
						 	from = grades[i];
							to = grades[i + 1];
							labels.push(
								' ' +
								text[i] );
						}
						div.innerHTML = labels.join('
');

				     return div;
				 };

				 legend.addTo(map);

		     })

    if (pymChild) {
        pymChild.sendHeight();
    }
}

/*
 * NB: Use window.load instead of document.ready
 * to ensure all images have loaded
 */
$(window).load(function() {
    pymChild = new pym.Child({
        renderCallback: render
    });
})
<|endoftext|>


<commit_before>var minimalUql = require("./e2e.minimal.js");
var urlTest = 'https://www.studenthub.uq.edu.au/workgroups/library-staff-development';

module.exports = {
  '@tags': ['e2etest', 'studenthub'],

  'Test reusable components are applied to StudentHub' : function (client) {
    // common uql checks
    minimalUql.commonChecks(client, urlTest);

    // studenthub specific checks
    client
      .url(urlTest)
      .assert.elementPresent('.sidebar .body > a', 'sidebar More Events button is present')
      .assert.containsText('.sidebar .body > a', 'MORE EVENTS')
      .end();

  }

};
<commit_msg>preset HTML to uppercase so nightwatch tests on edge can pass  [PT #129811221]<commit_after>var minimalUql = require("./e2e.minimal.js");
var urlTest = 'https://www.studenthub.uq.edu.au/workgroups/library-staff-development';

module.exports = {
  '@tags': ['e2etest', 'studenthub'],

  'Test reusable components are applied to StudentHub' : function (client) {
    // common uql checks
    minimalUql.commonChecks(client, urlTest);

    // studenthub specific checks
    client
      .url(urlTest)
      .assert.elementPresent('.sidebar .body > a', 'sidebar More Events button is present')
      .assert.containsText('.sidebar .body > a', 'MORE EVENTS') // works in all other browsers
//      .assert.containsText('.sidebar .body > a', 'More events') //works in edge
      .end();

  }

};
<|endoftext|>


<commit_before>
import create from './create.js';

/**
element(name, options)

- name: 'name'     Custom element tag name, eg. ''
- options: {
       extends:    Name of tag to extend, makes the element a custom built-in element
       mode:       'open' or 'closed', defaults to closed
       template:   String or template node or id used to create a shadow DOM
       attributes: A `{name: fn}` map called when named attributes change
       properties: A map of properties defined on the element prototype. Where
            the property `value` is defined extra work is done to make the
            element work inside a form
       construct:  Lifecycle handler called during element construction with shadow as first argument
       connect:    Lifecycle handler called when element added to DOM with shadow as first argument
       disconnect: Lifecycle handler called when element removed from DOM
       enable:     Lifecycle handler called when form element enabled
       disable:    Lifecycle handler called when form element disabled
       reset:      Lifecycle handler called when form element reset
       restore:    Lifecycle handler called when form element restored
   }
*/

const assign = Object.assign;

const constructors = {
    'a':        HTMLAnchorElement,
    'p':        HTMLParagraphElement,
    'br':       HTMLBRElement,
    'img':      HTMLImageElement,
    'template': HTMLTemplateElement
};

//const inputEvent = new CustomEvent('input', eventOptions);

const $internals = Symbol('internals');
const $shadow    = Symbol('shadow');

const formProperties = {
    // These properties echo those provided by native form controls.
    // They are not strictly necessary, but provided for consistency.
    type: { value: 'text' },

    name: {
        set: function(name) { return this.setAttribute('name', name); },
        get: function() { return this.getAttribute('name') || ''; }
    },

    form: { get: function() { return this[$internals].form; }},
    labels: { get: function() { return this[$internals].labels; }},
    validity: { get: function() { return this[$internals].validity; }},
    validationMessage: { get: function() { return this[$internals].validationMessage; }},
    willValidate: { get: function() { return this[$internals].willValidate; }},

    // Methods
    checkValidity: { value: function() { return this[$internals].checkValidity(); }},
    reportValidity: { value: function() { return this[$internals].reportValidity(); }}
};

function getElementConstructor(tag) {
        // Return a constructor from the known list of tag names – not all tags
        // have constructor names that match their tags
    return constructors[tag]
        // Or assemble the tag name in the form "HTMLTagElement" and return
        // that property of the window object
        || window['HTML' + tag[0].toUpperCase() + tag.slice(1) + 'Element']
        || (() => {
            throw new Error('Constructor not found for tag "' + tag + '"');
        })();
}

function transferProperty(elem, key) {
    if (elem.hasOwnProperty(key)) {
        const value = elem[key];
        delete elem[key];
        elem[key] = value;
    }

    return elem;
}

function getTemplateById(id) {
    const template = document.getElementById(id);

    if (!template || !template.content) {
        throw new Error('Template id="' + id + '" not found in document');
    }

    return template;
}

function createShadow(template, elem, options) {
    if (!template) { return; }

    // Create a shadow root if there is DOM content. Shadows may be 'open' or
    // 'closed'. Closed shadows are not exposed via element.shadowRoot, and
    // events propagating from inside of them report the element as target.
    const shadow = elem.attachShadow({ mode: options.mode || 'closed' }) ;
    elem[$shadow] = shadow;

    // If template is a <template>
    if (typeof template === 'string') {
        shadow.innerHTML = template;
    }
    else {
        shadow.appendChild(template.content.cloneNode(true));
    }

    return shadow;
}

function attachInternals(elem) {
    // Use native attachInternals where it exists
    if (elem.attachInternals) {
        return elem.attachInternals();
    }

    // Otherwise polyfill it with a pseudo internals object, actually a hidden
    // input that we put inside element (but outside the shadow DOM)
    const hidden = create('input', { type: 'hidden', name: elem.name });
    elem.appendChild(hidden);

    // Polyfill internals object setFormValue
    hidden.setFormValue = function(value) {
        this.value = value;
    };

    return hidden;
}

export default function element(name, options) {

    // Get the element constructor from options.extends, or the
    // base HTMLElement constructor
    const constructor = options.extends ?
        getElementConstructor(options.extends) :
        HTMLElement ;

    const template = options && options.template && (
        typeof options.template === 'string' ?
            // If options.template is an #id, search for <template id="id">
            options.template[0] === '#' ? getTemplateById(options.template.slice(1)) :
            // It must be a string of HTML
            options.template :
        options.template.content ?
            // It must be a template node
            options.template :
        // Whatever it is, we don't support it
        function(){
            throw new Error('element() options.template not recognised as template node, id or string');
        }()
    );

    function Element() {
        // Construct an instance from Constructor using the Element prototype
        const elem   = Reflect.construct(constructor, arguments, Element);
        const shadow = createShadow(template, elem, options);

        if (Element.formAssociated) {
            // Get access to the internal form control API
            elem[$internals] = attachInternals(elem);
        }

        options.construct && options.construct.call(elem, shadow);

        // At this point, if properties have already been set before the
        // element was upgraded, they exist on the elem itself, where we have
        // just upgraded it's protytype to define those properties those
        // definitions will never be reached. Either:
        //
        // 1. Define properties on the instance instead of the prototype
        //    Object.defineProperties(elem, properties);
        //
        // 2. Take a great deal of care not to set properties before an element
        //    is upgraded. I can't impose a restriction like that.
        //
        // 3. Copy defined properties to their prototype handlers and delete
        //    them on the instance.
        //
        // Let's go with 3. I'm not happy you have to do this, though.
        options.properties
        && Object.keys(options.properties).reduce(transferProperty, elem);

        return elem;
    }

    // options.properties
    //
    // Map of getter/setters called when properties mutate. Must be defined
    // before attributeChangedCallback, but I'm not sure why right now.
    //
    // {
    //     name: { get: fn, set: fn }
    // }
    //
    // Where one of the properties is `value`, this element is set up as a form
    // element.

    if (options.properties && options.properties.value) {
        // Flag the Element class as formAssociated
        Element.formAssociated = true;

        Element.prototype = Object.create(constructor.prototype, assign({}, formProperties, options.properties, {
            value: {
                get: options.properties.value.get,
                set: function() {
                    // After setting value
                    options.properties.value.set.apply(this, arguments);

                    // Copy it to internal form state
                    this[$internals].setFormValue('' + this.value);
                }
            }
        })) ;
    }
    else {
        Element.prototype = Object.create(constructor.prototype, options.properties || {}) ;
    }

    // options.attributes
    //
    // Map of functions called when named attributes change.
    //
    // {
    //     name: fn
    // }

    if (options.attributes) {
        Element.observedAttributes = Object.keys(options.attributes);

        Element.prototype.attributeChangedCallback = function(name, old, value) {
            options.attributes[name].call(this, value, name);
        };
    }

    // Lifecycle
    // https://html.spec.whatwg.org/multipage/custom-elements.html#custom-element-reactions
    //
    // More lifecycle reactions are available in the spec:
    // adoptedCallback
    // formAssociatedCallback

    if (options.connect) {
        // Pass shadow to connect(shadow) function
        Element.prototype.connectedCallback = function() {
            return options.connect.call(this, this[$shadow]);

            // 'input' events are suppused to traverse the shadow boundary
            // but they do not. At least not in Chrome 2019 - a
            /*this[$shadow].addEventListener('input', (e) => {
                if (!e.composed) {
                    console.warn('Custom element not allowing input event to traverse shadow boundary');
                    this.dispatchEvent(inputEvent);
                }
            });*/
        };
    }

    if (options.disconnect) {
        Element.prototype.disconnectedCallback = function() {
            return options.disconnect.call(this, this[$shadow]);
        };
    }

    // Form lifecycle

    if (Element.formAssociated) {
        if (options.enable || options.disable) {
            Element.prototype.formDisabledCallback = function(disabled) {
                return disabled ?
                    options.disable && options.disable.call(this, this[$shadow]) :
                    options.enable && options.enable.call(this, this[$shadow]) ;
            };
        }

        if (options.reset) {
            Element.prototype.formResetCallback = function() {
                return options.reset.call(this, this[$shadow]);
            };
        }

        if (options.restore) {
            Element.prototype.formStateRestoreCallback = function() {
                return options.restore.call(this, this[$shadow]);
            };
        }
    }

    // Define element

    window.customElements.define(name, Element, options);
    return Element;
}
<commit_msg>Element<commit_after>
import create from './create.js';

/**
element(name, options)

- name: 'name'     Custom element tag name, eg. ''
- options: {
       extends:    Name of tag to extend, makes the element a custom built-in element
       mode:       'open' or 'closed', defaults to closed
       template:   String or template node or id used to create a shadow DOM
       attributes: A `{name: fn}` map called when named attributes change
       properties: A map of properties defined on the element prototype. Where
            the property `value` is defined extra work is done to make the
            element work inside a form
       construct:  Lifecycle handler called during element construction with shadow as first argument
       connect:    Lifecycle handler called when element added to DOM with shadow as first argument
       disconnect: Lifecycle handler called when element removed from DOM
       enable:     Lifecycle handler called when form element enabled
       disable:    Lifecycle handler called when form element disabled
       reset:      Lifecycle handler called when form element reset
       restore:    Lifecycle handler called when form element restored
   }
*/

const assign = Object.assign;

const constructors = {
    'a':        HTMLAnchorElement,
    'p':        HTMLParagraphElement,
    'br':       HTMLBRElement,
    'img':      HTMLImageElement,
    'template': HTMLTemplateElement
};

//const inputEvent = new CustomEvent('input', eventOptions);

const $internals = Symbol('internals');
const $shadow    = Symbol('shadow');

const formProperties = {
    // These properties echo those provided by native form controls.
    // They are not strictly necessary, but provided for consistency.
    type: { value: 'text' },

    name: {
        set: function(name) { return this.setAttribute('name', name); },
        get: function() { return this.getAttribute('name') || ''; }
    },

    form: { get: function() { return this[$internals].form; }},
    labels: { get: function() { return this[$internals].labels; }},
    validity: { get: function() { return this[$internals].validity; }},
    validationMessage: { get: function() { return this[$internals].validationMessage; }},
    willValidate: { get: function() { return this[$internals].willValidate; }},

    // Methods
    checkValidity: { value: function() { return this[$internals].checkValidity(); }},
    reportValidity: { value: function() { return this[$internals].reportValidity(); }}
};

function getElementConstructor(tag) {
        // Return a constructor from the known list of tag names – not all tags
        // have constructor names that match their tags
    return constructors[tag]
        // Or assemble the tag name in the form "HTMLTagElement" and return
        // that property of the window object
        || window['HTML' + tag[0].toUpperCase() + tag.slice(1) + 'Element']
        || (() => {
            throw new Error('Constructor not found for tag "' + tag + '"');
        })();
}

function transferProperty(elem, key) {
    if (elem.hasOwnProperty(key)) {
        const value = elem[key];
        delete elem[key];
        elem[key] = value;
    }

    return elem;
}

function getTemplateById(id) {
    const template = document.getElementById(id);

    if (!template || !template.content) {
        throw new Error('Template id="' + id + '" not found in document');
    }

    return template;
}

function createShadow(template, elem, options) {
    if (!template) { return; }

    // Create a shadow root if there is DOM content. Shadows may be 'open' or
    // 'closed'. Closed shadows are not exposed via element.shadowRoot, and
    // events propagating from inside of them report the element as target.
    const shadow = elem.attachShadow({
        mode: options.mode || 'closed',
        delegatesFocus: true
    });

    elem[$shadow] = shadow;

    // If template is a <template>
    if (typeof template === 'string') {
        shadow.innerHTML = template;
    }
    else {
        shadow.appendChild(template.content.cloneNode(true));
    }

    return shadow;
}

function attachInternals(elem) {
    // Use native attachInternals where it exists
    if (elem.attachInternals) {
        return elem.attachInternals();
    }

    // Otherwise polyfill it with a pseudo internals object, actually a hidden
    // input that we put inside element (but outside the shadow DOM)
    const hidden = create('input', { type: 'hidden', name: elem.name });
    elem.appendChild(hidden);

    // Polyfill internals object setFormValue
    hidden.setFormValue = function(value) {
        this.value = value;
    };

    return hidden;
}

export default function element(name, options) {

    // Get the element constructor from options.extends, or the
    // base HTMLElement constructor
    const constructor = options.extends ?
        getElementConstructor(options.extends) :
        HTMLElement ;

    const template = options && options.template && (
        typeof options.template === 'string' ?
            // If options.template is an #id, search for <template id="id">
            options.template[0] === '#' ? getTemplateById(options.template.slice(1)) :
            // It must be a string of HTML
            options.template :
        options.template.content ?
            // It must be a template node
            options.template :
        // Whatever it is, we don't support it
        function(){
            throw new Error('element() options.template not recognised as template node, id or string');
        }()
    );

    function Element() {
        // Construct an instance from Constructor using the Element prototype
        const elem   = Reflect.construct(constructor, arguments, Element);
        const shadow = createShadow(template, elem, options);

        if (Element.formAssociated) {
            // Get access to the internal form control API
            elem[$internals] = attachInternals(elem);
        }

        options.construct && options.construct.call(elem, shadow);

        // At this point, if properties have already been set before the
        // element was upgraded, they exist on the elem itself, where we have
        // just upgraded it's protytype to define those properties those
        // definitions will never be reached. Either:
        //
        // 1. Define properties on the instance instead of the prototype
        //    Object.defineProperties(elem, properties);
        //
        // 2. Take a great deal of care not to set properties before an element
        //    is upgraded. I can't impose a restriction like that.
        //
        // 3. Copy defined properties to their prototype handlers and delete
        //    them on the instance.
        //
        // Let's go with 3. I'm not happy you have to do this, though.
        options.properties
        && Object.keys(options.properties).reduce(transferProperty, elem);

        return elem;
    }

    // options.properties
    //
    // Map of getter/setters called when properties mutate. Must be defined
    // before attributeChangedCallback, but I'm not sure why right now.
    //
    // {
    //     name: { get: fn, set: fn }
    // }
    //
    // Where one of the properties is `value`, this element is set up as a form
    // element.

    if (options.properties && options.properties.value) {
        // Flag the Element class as formAssociated
        Element.formAssociated = true;

        Element.prototype = Object.create(constructor.prototype, assign({}, formProperties, options.properties, {
            value: {
                get: options.properties.value.get,
                set: function() {
                    // After setting value
                    options.properties.value.set.apply(this, arguments);

                    // Copy it to internal form state
                    this[$internals].setFormValue('' + this.value);
                }
            }
        })) ;
    }
    else {
        Element.prototype = Object.create(constructor.prototype, options.properties || {}) ;
    }

    // options.attributes
    //
    // Map of functions called when named attributes change.
    //
    // {
    //     name: fn
    // }

    if (options.attributes) {
        Element.observedAttributes = Object.keys(options.attributes);

        Element.prototype.attributeChangedCallback = function(name, old, value) {
            options.attributes[name].call(this, value, name);
        };
    }

    // Lifecycle
    // https://html.spec.whatwg.org/multipage/custom-elements.html#custom-element-reactions
    //
    // More lifecycle reactions are available in the spec:
    // adoptedCallback
    // formAssociatedCallback

    if (options.connect) {
        // Pass shadow to connect(shadow) function
        Element.prototype.connectedCallback = function() {
            return options.connect.call(this, this[$shadow]);

            // 'input' events are suppused to traverse the shadow boundary
            // but they do not. At least not in Chrome 2019 - a
            /*this[$shadow].addEventListener('input', (e) => {
                if (!e.composed) {
                    console.warn('Custom element not allowing input event to traverse shadow boundary');
                    this.dispatchEvent(inputEvent);
                }
            });*/
        };
    }

    if (options.disconnect) {
        Element.prototype.disconnectedCallback = function() {
            return options.disconnect.call(this, this[$shadow]);
        };
    }

    // Form lifecycle

    if (Element.formAssociated) {
        if (options.enable || options.disable) {
            Element.prototype.formDisabledCallback = function(disabled) {
                return disabled ?
                    options.disable && options.disable.call(this, this[$shadow]) :
                    options.enable && options.enable.call(this, this[$shadow]) ;
            };
        }

        if (options.reset) {
            Element.prototype.formResetCallback = function() {
                return options.reset.call(this, this[$shadow]);
            };
        }

        if (options.restore) {
            Element.prototype.formStateRestoreCallback = function() {
                return options.restore.call(this, this[$shadow]);
            };
        }
    }

    // Define element

    window.customElements.define(name, Element, options);
    return Element;
}
<|endoftext|>


<commit_before>/**
 * @fileoverview
This module provides an implementation of "fs-base" as
 * defined by the CommonJS
 * Filesystem/A/0 proposal.
 */

include('io');
include('binary');
require('core/array');

module.shared = true;

export('canonical',
       'changeWorkingDirectory',
       'workingDirectory',
       'exists',
       'isDirectory',
       'isFile',
       'isReadable',
       'isWritable',
       'list',
       'makeDirectory',
       'move',
       'lastModified',
       'openRaw',
       'remove',
       'removeDirectory',
       'size',
       'touch',
       'symbolicLink',
       'hardLink',
       'readLink',
       'isLink',
       'same',
       'sameFilesystem',
       'iterate',
       'Permissions',
       'owner',
       'group',
       'changePermissions',
       'changeOwner',
       'changeGroup',
       'permissions');

var File = java.io.File,
    FileInputStream = java.io.FileInputStream,
    FileOutputStream = java.io.FileOutputStream;

var SEPARATOR = File.separator;

var POSIX;

function getPOSIX() {
    POSIX = POSIX || org.ringojs.wrappers.POSIX.getPOSIX();
    return POSIX;
}

function openRaw(path, mode, permissions) {
    // TODO many things missing here
    var file = resolveFile(path);
    mode = mode || {};
    var {read, write, append, create, exclusive, truncate} = mode;
    if (!read && !write && !append) {
        read = true;
    }
    if (read) {
        return new Stream(new FileInputStream(file));
    } else {
        return new Stream(FileOutputStream(file, Boolean(append)));
    }
}

function move(from, to) {
    var source = resolveFile(from);
    var target = resolveFile(to);
    if (!source.renameTo(target)) {
        throw new Error("failed to move file from " + from + " to " + to);
    }
}

function remove(path) {
    var file = resolveFile(path);
    if (!file['delete']()) {
        throw new Error("failed to remove file " + path);
    }
}

function exists(path) {
    var file = resolveFile(path);
    return file.exists();
}

function workingDirectory() {
    return java.lang.System.getProperty('user.dir') + SEPARATOR;
}

function changeWorkingDirectory(path) {
    path = new File(path).getCanonicalPath();
    java.lang.System.setProperty('user.dir', path);
}

function removeDirectory(path) {
    var file = resolveFile(path);
    if (!file['delete']()) {
        throw new Error("failed to remove directory " + path);
    }
}

function list(path) {
    var file = resolveFile(path);
    var list = file.list();
    if (list == null) {
        throw new Error("failed to list directory " + path);
    }
    var result = [];
    for (var i = 0; i < list.length; i++) {
        result[i] = list[i];
    }
    return result;
}

function size(path) {
    var file = resolveFile(path);
    return file.length();
}

function lastModified(path) {
    var file = resolveFile(path);
    return new Date(file.lastModified());
}

function makeDirectory(path) {
    // TODO support permissions, use posix
    var file = resolveFile(path);
    if (!file.isDirectory() && !file.mkdir()) {
        throw new Error("failed to make directory " + path);
    }
}

function isReadable(path) {
    return resolveFile(path).canRead();
}

function isWritable(path) {
    return resolveFile(path).canWrite();
}

function isFile(path) {
    return resolveFile(path).isFile();
}

function isDirectory(path) {
    return resolveFile(path).isDirectory();
}

function isLink(target) {
    var POSIX = getPOSIX();
    var stat = POSIX.lstat(target);
    return stat.isSymlink();
}

function same(pathA, pathB) {
    var POSIX = getPOSIX();
    var stat1 = POSIX.stat(pathA);
    var stat2 = POSIX.stat(pathB);
    return stat1.isIdentical(stat2);
}

function sameFilesystem(pathA, pathB) {
    var POSIX = getPOSIX();
    var stat1 = POSIX.stat(pathA);
    var stat2 = POSIX.stat(pathB);
    return stat1.dev() == stat2.dev();
}

function canonical(path) {
    return resolveFile(path).getCanonicalPath();
}

function touch(path, mtime) {
    mtime = mtime || Date.now();
    return resolveFile(path).setLastModified(mtime);
}

function symbolicLink(source, target) {
    var POSIX = getPOSIX();
    return POSIX.symlink(source, target);
}

function hardLink(source, target) {
    var POSIX = getPOSIX();
    return POSIX.link(source, target);
}

function readLink(path) {
    var POSIX = getPOSIX();
    return POSIX.readlink(path);
}

function iterate(path) {
    var iter = function() {
        for each (var item in list(path)) {
            yield item;
        }
        throw StopIteration;
    }();
    // spec requires iterator(), native iterators/generators only have __iterator__().
    iter.iterator = iter.__iterator__;
    return iter;
}

function Permissions(permissions, constructor) {
    if (!(this instanceof Permissions)) {
        return new Permissions(permissions, constructor);
    }
    if (!Permissions['default']) {
        try {
            var POSIX = getPOSIX();
            // FIXME: no way to get umask without setting it?
            var umask = POSIX.umask(0022);
            if (umask != 0022) {
                POSIX.umask(umask);
            }
            Permissions['default'] = new Permissions(~umask & 0777);
        } catch (error) {
            Permissions['default'] = new Permissions(0755);
        }
    }
    this.update(Permissions['default']);
    this.update(permissions);
    this.constructor = constructor;
}

Permissions.prototype.update = function(permissions) {
    var fromNumber = typeof permissions == 'number';
    if (!fromNumber && !(permissions instanceof Object)) {
        return;
    }
    for each (var user in ['owner', 'group', 'other']) {
        this[user] = this[user] || {};
        for each (var perm in ['read', 'write', 'execute']) {
            this[user][perm] = fromNumber ?
                Boolean((permissions <<= 1) & 512) :
                Boolean(permissions[user] && permissions[user][perm]);
        }
    }
};

Permissions.prototype.toNumber = function() {
    var result = 0;
    for each (var user in ['owner', 'group', 'other']) {
        for each (var perm in ['read', 'write', 'execute']) {
            result <<= 1;
            result |= +this[user][perm];
        }
    }
    return result;
};

function permissions(path) {
    var POSIX = getPOSIX();
    var stat = POSIX.stat(path);
    return new Permissions(stat.mode() & 0777);
}

function owner(path) {
    try {
        var POSIX = getPOSIX();
        var uid = POSIX.stat(path).uid();
        var owner = POSIX.getpwuid(uid);
        return owner ? owner.pw_name : uid;
    } catch (error) {
        return null;
    }
}

function group(path) {
    try {
        var POSIX = getPOSIX();
        var gid = POSIX.stat(path).gid();
        var group = POSIX.getgrgid(gid);
        return group ? group.gr_name : gid;
    } catch (error) {
        return null;
    }
}

function changePermissions(path, permissions) {
    permissions = new Permissions(permissions);
    var POSIX = getPOSIX();
    var stat = POSIX.stat(path);
    // do not overwrite set-UID bits etc
    var preservedBits = stat.mode() & 07000;
    var newBits = permissions.toNumber();
    POSIX.chmod(path, preservedBits | newBits);
}

// Supports user name string as well as uid int input.
function changeOwner(path, user) {
    var POSIX = getPOSIX();
    return POSIX.chown(path, typeof user === 'string' ?
            POSIX.getpwnam(user).pw_uid : user, -1);
}

// Supports group name string as well as gid int input.
function changeGroup(path, group) {
    var POSIX = getPOSIX();
    return POSIX.chown(path, -1, typeof group === 'string' ?
            POSIX.getgrnam(group).gr_gid : group);
}

var optionsMask = {
    read: 1,
    write: 1,
    append: 1,
    update: 1,
    binary: 1,
    exclusive: 1,
    canonical: 1,
    charset: 1
};

function checkOptions(mode, options) {
    if (!options) {
        options = {};
    } else if (typeof options != 'object') {
        if (typeof options == 'string') {
            // if options is a mode string convert it to options object
            options = applyMode(options);
        } else {
            throw new Error('unsupported options argument');
        }
    } else {
        // run sanity check on user-provided options object
        for (var key in options) {
            if (!(key in optionsMask)) {
                throw new Error("unsupported option: " + key);
            }
            options[key] = key == 'charset' ?
                    String(options[key]) : Boolean(options[key]);
        }
    }
    if (typeof mode == 'string') {
        // apply mode string to options object
        applyMode(mode, options);
    }
    return options;
}

// apply mode string to options object
function applyMode(mode, options) {
    options = options || {};
    for (var i = 0; i < mode.length; i++) {
        switch (mode[i]) {
        case 'r':
            options.read = true;
            break;
        case 'w':
            options.write = true;
            break;
        case 'a':
            options.append = true;
            break;
        case '+':
            options.update = true;
            break;
        case 'b':
            options.binary = true;
            break;
        case 'x':
            options.exclusive = true;
            break;
        case 'c':
            options.canonical = true;
            break;
        default:
            throw new Error("unsupported mode argument: " + options);
        }
    }
    return options;
}

function resolveFile(path) {
    // Fix for http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4117557
    // relative files are not resolved against workingDirectory/user.dir in java,
    // making the file absolute makes sure it is resolved correctly.
    if (path == undefined) {
        throw new Error('undefined path argument');
    }
    var file = file instanceof File ? file : new File(String(path));
    return file.isAbsolute() ? file : file.getAbsoluteFile();
}
<commit_msg>Fix infinite recursion in Permissions constructor (undo lazy POSIX)<commit_after>/**
 * @fileoverview
This module provides an implementation of "fs-base" as
 * defined by the CommonJS
 * Filesystem/A/0 proposal.
 */

include('io');
include('binary');
require('core/array');

module.shared = true;

export('canonical',
       'changeWorkingDirectory',
       'workingDirectory',
       'exists',
       'isDirectory',
       'isFile',
       'isReadable',
       'isWritable',
       'list',
       'makeDirectory',
       'move',
       'lastModified',
       'openRaw',
       'remove',
       'removeDirectory',
       'size',
       'touch',
       'symbolicLink',
       'hardLink',
       'readLink',
       'isLink',
       'same',
       'sameFilesystem',
       'iterate',
       'Permissions',
       'owner',
       'group',
       'changePermissions',
       'changeOwner',
       'changeGroup',
       'permissions');

var File = java.io.File,
    FileInputStream = java.io.FileInputStream,
    FileOutputStream = java.io.FileOutputStream;

var SEPARATOR = File.separator;

var POSIX;

function getPOSIX() {
    POSIX = POSIX || org.ringojs.wrappers.POSIX.getPOSIX();
    return POSIX;
}

function openRaw(path, mode, permissions) {
    // TODO many things missing here
    var file = resolveFile(path);
    mode = mode || {};
    var {read, write, append, create, exclusive, truncate} = mode;
    if (!read && !write && !append) {
        read = true;
    }
    if (read) {
        return new Stream(new FileInputStream(file));
    } else {
        return new Stream(FileOutputStream(file, Boolean(append)));
    }
}

function move(from, to) {
    var source = resolveFile(from);
    var target = resolveFile(to);
    if (!source.renameTo(target)) {
        throw new Error("failed to move file from " + from + " to " + to);
    }
}

function remove(path) {
    var file = resolveFile(path);
    if (!file['delete']()) {
        throw new Error("failed to remove file " + path);
    }
}

function exists(path) {
    var file = resolveFile(path);
    return file.exists();
}

function workingDirectory() {
    return java.lang.System.getProperty('user.dir') + SEPARATOR;
}

function changeWorkingDirectory(path) {
    path = new File(path).getCanonicalPath();
    java.lang.System.setProperty('user.dir', path);
}

function removeDirectory(path) {
    var file = resolveFile(path);
    if (!file['delete']()) {
        throw new Error("failed to remove directory " + path);
    }
}

function list(path) {
    var file = resolveFile(path);
    var list = file.list();
    if (list == null) {
        throw new Error("failed to list directory " + path);
    }
    var result = [];
    for (var i = 0; i < list.length; i++) {
        result[i] = list[i];
    }
    return result;
}

function size(path) {
    var file = resolveFile(path);
    return file.length();
}

function lastModified(path) {
    var file = resolveFile(path);
    return new Date(file.lastModified());
}

function makeDirectory(path) {
    // TODO support permissions, use posix
    var file = resolveFile(path);
    if (!file.isDirectory() && !file.mkdir()) {
        throw new Error("failed to make directory " + path);
    }
}

function isReadable(path) {
    return resolveFile(path).canRead();
}

function isWritable(path) {
    return resolveFile(path).canWrite();
}

function isFile(path) {
    return resolveFile(path).isFile();
}

function isDirectory(path) {
    return resolveFile(path).isDirectory();
}

function isLink(target) {
    var POSIX = getPOSIX();
    var stat = POSIX.lstat(target);
    return stat.isSymlink();
}

function same(pathA, pathB) {
    var POSIX = getPOSIX();
    var stat1 = POSIX.stat(pathA);
    var stat2 = POSIX.stat(pathB);
    return stat1.isIdentical(stat2);
}

function sameFilesystem(pathA, pathB) {
    var POSIX = getPOSIX();
    var stat1 = POSIX.stat(pathA);
    var stat2 = POSIX.stat(pathB);
    return stat1.dev() == stat2.dev();
}

function canonical(path) {
    return resolveFile(path).getCanonicalPath();
}

function touch(path, mtime) {
    mtime = mtime || Date.now();
    return resolveFile(path).setLastModified(mtime);
}

function symbolicLink(source, target) {
    var POSIX = getPOSIX();
    return POSIX.symlink(source, target);
}

function hardLink(source, target) {
    var POSIX = getPOSIX();
    return POSIX.link(source, target);
}

function readLink(path) {
    var POSIX = getPOSIX();
    return POSIX.readlink(path);
}

function iterate(path) {
    var iter = function() {
        for each (var item in list(path)) {
            yield item;
        }
        throw StopIteration;
    }();
    // spec requires iterator(), native iterators/generators only have __iterator__().
    iter.iterator = iter.__iterator__;
    return iter;
}

function Permissions(permissions, constructor) {
    if (!(this instanceof Permissions)) {
        return new Permissions(permissions, constructor);
    }
    this.update(Permissions['default']);
    this.update(permissions);
    this.constructor = constructor;
}

Permissions.prototype.update = function(permissions) {
    var fromNumber = typeof permissions == 'number';
    if (!fromNumber && !(permissions instanceof Object)) {
        return;
    }
    for each (var user in ['owner', 'group', 'other']) {
        this[user] = this[user] || {};
        for each (var perm in ['read', 'write', 'execute']) {
            this[user][perm] = fromNumber ?
                Boolean((permissions <<= 1) & 512) :
                Boolean(permissions[user] && permissions[user][perm]);
        }
    }
};

Permissions.prototype.toNumber = function() {
    var result = 0;
    for each (var user in ['owner', 'group', 'other']) {
        for each (var perm in ['read', 'write', 'execute']) {
            result <<= 1;
            result |= +this[user][perm];
        }
    }
    return result;
};

if (!Permissions['default']) {
    try {
        var POSIX = getPOSIX();
        // FIXME: no way to get umask without setting it?
        var umask = POSIX.umask(0022);
        if (umask != 0022) {
            POSIX.umask(umask);
        }
        Permissions['default'] = new Permissions(~umask & 0777);
    } catch (error) {
        Permissions['default'] = new Permissions(0755);
    }
}

function permissions(path) {
    var POSIX = getPOSIX();
    var stat = POSIX.stat(path);
    return new Permissions(stat.mode() & 0777);
}

function owner(path) {
    try {
        var POSIX = getPOSIX();
        var uid = POSIX.stat(path).uid();
        var owner = POSIX.getpwuid(uid);
        return owner ? owner.pw_name : uid;
    } catch (error) {
        return null;
    }
}

function group(path) {
    try {
        var POSIX = getPOSIX();
        var gid = POSIX.stat(path).gid();
        var group = POSIX.getgrgid(gid);
        return group ? group.gr_name : gid;
    } catch (error) {
        return null;
    }
}

function changePermissions(path, permissions) {
    permissions = new Permissions(permissions);
    var POSIX = getPOSIX();
    var stat = POSIX.stat(path);
    // do not overwrite set-UID bits etc
    var preservedBits = stat.mode() & 07000;
    var newBits = permissions.toNumber();
    POSIX.chmod(path, preservedBits | newBits);
}

// Supports user name string as well as uid int input.
function changeOwner(path, user) {
    var POSIX = getPOSIX();
    return POSIX.chown(path, typeof user === 'string' ?
            POSIX.getpwnam(user).pw_uid : user, -1);
}

// Supports group name string as well as gid int input.
function changeGroup(path, group) {
    var POSIX = getPOSIX();
    return POSIX.chown(path, -1, typeof group === 'string' ?
            POSIX.getgrnam(group).gr_gid : group);
}

var optionsMask = {
    read: 1,
    write: 1,
    append: 1,
    update: 1,
    binary: 1,
    exclusive: 1,
    canonical: 1,
    charset: 1
};

function checkOptions(mode, options) {
    if (!options) {
        options = {};
    } else if (typeof options != 'object') {
        if (typeof options == 'string') {
            // if options is a mode string convert it to options object
            options = applyMode(options);
        } else {
            throw new Error('unsupported options argument');
        }
    } else {
        // run sanity check on user-provided options object
        for (var key in options) {
            if (!(key in optionsMask)) {
                throw new Error("unsupported option: " + key);
            }
            options[key] = key == 'charset' ?
                    String(options[key]) : Boolean(options[key]);
        }
    }
    if (typeof mode == 'string') {
        // apply mode string to options object
        applyMode(mode, options);
    }
    return options;
}

// apply mode string to options object
function applyMode(mode, options) {
    options = options || {};
    for (var i = 0; i < mode.length; i++) {
        switch (mode[i]) {
        case 'r':
            options.read = true;
            break;
        case 'w':
            options.write = true;
            break;
        case 'a':
            options.append = true;
            break;
        case '+':
            options.update = true;
            break;
        case 'b':
            options.binary = true;
            break;
        case 'x':
            options.exclusive = true;
            break;
        case 'c':
            options.canonical = true;
            break;
        default:
            throw new Error("unsupported mode argument: " + options);
        }
    }
    return options;
}

function resolveFile(path) {
    // Fix for http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4117557
    // relative files are not resolved against workingDirectory/user.dir in java,
    // making the file absolute makes sure it is resolved correctly.
    if (path == undefined) {
        throw new Error('undefined path argument');
    }
    var file = file instanceof File ? file : new File(String(path));
    return file.isAbsolute() ? file : file.getAbsoluteFile();
}
<|endoftext|>


<commit_before>/* jshint -W097 */// jshint strict:false
/*jslint node: true */

"use strict";
var utils       = require(__dirname + '/lib/utils'); // Get common adapter utils
var parseString = require('xml2js').parseString;
var request     = require('request');

var lang = 'de';

var adapter = utils.adapter({
    name:           'tvspielfilm',
    systemConfig:   true,
    useFormatDate:  true
});

adapter.on('ready', function () {
    adapter.getForeignObject('system.config', function (err, data) {
        if (data && data.common) {
            lang  = data.common.language;
        }

        adapter.log.debug('adapter tvspielfilm initializing objects');
        main();
        adapter.log.info('adapter tvspielfilm objects written');

        setTimeout(function () {
            adapter.log.info('force terminating after 1 minute');
            adapter.stop();
        }, 60000);

    });
});

function readSettings() {
    if (adapter.config.blacklist === undefined || adapter.config.blacklist.length === 0) adapter.log.info('Keine Stationen zur Blacklist hinzugefügt');
    else adapter.log.info('Zahl Stationen in Blacklist: ' + adapter.config.blacklist.length);
    // eigentlich wird erst in check_sender das setting (config.blacklist) eingelesen

    for (var s in adapter.config.blacklist) {
        adapter.log.info('Blacklist (#' + (s+1) + '): ' + adapter.config.blacklist[s]);
    }
}

function check_sender (show) { //  wird so übergeben "16:50 | Sky Cinema | Kill the Boss 2"
    var show_info = show.split(' | ');

    // es können noch weitere Daten extrahiert und geprüft werden:
    // showtime_info = show_info[0].split(':');
    // showtime = new Date ();
    // showtime.setHours(parseInt(showtime_info[0],10));   // -> 16
    // showtime.setMinutes(parseInt(showtime_info[1],10)); // -> 50
    // Vergleich mit aktueller zeit möglich....

    // Suche nach Filmen genauso möglich
    // movie = show_info[2];

    // Suche nach Sender
    var station = show_info[1];
    var station_in_blacklist = (adapter.config.blacklist.indexOf(station,0) == -1) ? true : false; // Sender nicht in der Blacklist, also empfangbar
    return(station_in_blacklist);
}

var rss_options = {
    jetzt : { feedname: 'Jetzt',
              url: 'http://www.tvspielfilm.de/tv-programm/rss/jetzt.xml',
              state: 'rss.jetzt',
              cssclass:  'tv_jetzt'
            },
    tipps:  { feedname: 'Tipps',
              url: 'http://www.tvspielfilm.de/tv-programm/rss/tipps.xml',
              state: 'rss.tipps',
              cssclass:  'tv_tipps'
            }
}

function readFeed (x) {
    var link = rss_options[x].url;
    adapter.log.info('RSS Feed wird eingelesen: ' + link);
    request(link, function (error, response, body) {
        if (!error && response.statusCode == 200) {

            parseString(body, {
                explicitArray: false,
                mergeAttrs: true
            }, function (err, result) {
                var data = JSON.stringify(result, null, 2);
                var table = [];
                if (err) {
                    adapter.log.warn("Fehler: " + err);
                } else {
                    var sender_empfangbar = false;
                    if (result.rss.channel.item.length !== null) { // gelegentlicher Fehler bei nächtlicher Abfrage durch length (undefined) soll hier abgefangen werden
                        // Array durchzaehlen von 0 bis Zahl der items
                        for(var i = 0; i < result.rss.channel.item.length; i++) {
                            sender_empfangbar = check_sender(result.rss.channel.item[i].title);
                            if (sender_empfangbar) {
                                var entry = {
                                    image: result.rss.channel.item[i].enclosure ? '' : '',
                                    text:  '

' + result.rss.channel.item[i].title +
                                       '

' +
                                       result.rss.channel.item[i].description +'

',
                                    _Bild: result.rss.channel.item[i].enclosure ? '' : 'no image'
                                };
                                table.push(entry);
                            } // Ende Abfrage, ob Sender empfangbar
                        }
                    } else adapter.log.warn('LENGTH in TV Programm (' + rss_options[x].feedname + ') nicht definiert'); // ende if ungleich
                }
                adapter.setState(rss_options[x].state, {val: JSON.stringify(table), ack: true});// ganze XML in Objekt für Table Widget
            });
        } else adapter.log.warn(error,'error');
    });   // Ende request
    adapter.log.info('XML-Daten aus TV Spielfilm (' + rss_options[x].feedname + ') eingelesen');
}
function main() {
    readSettings();
    for (var j in rss_options) {
        readFeed(j);
    }
    adapter.stop();
}
<commit_msg>Weitere Feeds hinzugefügt<commit_after>/* jshint -W097 */// jshint strict:false
/*jslint node: true */

"use strict";
var utils       = require(__dirname + '/lib/utils'); // Get common adapter utils
var parseString = require('xml2js').parseString;
var request     = require('request');

var lang = 'de';

var adapter = utils.adapter({
    name:           'tvspielfilm',
    systemConfig:   true,
    useFormatDate:  true
});

adapter.on('ready', function () {
    adapter.getForeignObject('system.config', function (err, data) {
        if (data && data.common) {
            lang  = data.common.language;
        }

        adapter.log.debug('adapter tvspielfilm initializing objects');
        main();
        adapter.log.info('adapter tvspielfilm objects written');

        setTimeout(function () {
            adapter.log.info('force terminating after 1 minute');
            adapter.stop();
        }, 60000);

    });
});

function readSettings() {
    if (adapter.config.blacklist === undefined || adapter.config.blacklist.length === 0) adapter.log.info('Keine Stationen zur Blacklist hinzugefügt');
    else adapter.log.info('Zahl Stationen in Blacklist: ' + adapter.config.blacklist.length);
    // eigentlich wird erst in check_sender das setting (config.blacklist) eingelesen

    for (var s in adapter.config.blacklist) {
        adapter.log.info('Blacklist (#' + (s+1) + '): ' + adapter.config.blacklist[s]);
    }
}

function check_sender (show) { //  wird so übergeben "16:50 | Sky Cinema | Kill the Boss 2"
    var show_info = show.split(' | ');

    // es können noch weitere Daten extrahiert und geprüft werden:
    // showtime_info = show_info[0].split(':');
    // showtime = new Date ();
    // showtime.setHours(parseInt(showtime_info[0],10));   // -> 16
    // showtime.setMinutes(parseInt(showtime_info[1],10)); // -> 50
    // Vergleich mit aktueller zeit möglich....

    // Suche nach Filmen genauso möglich
    // movie = show_info[2];

    // Suche nach Sender
    var station = show_info[1];
    var station_in_blacklist = (adapter.config.blacklist.indexOf(station,0) == -1) ? true : false; // Sender nicht in der Blacklist, also empfangbar
    return(station_in_blacklist);
}

var rss_options = {
    jetzt :        { feedname: 'Jetzt',
                     url: 'http://www.tvspielfilm.de/tv-programm/rss/jetzt.xml',
                     state: 'rss.jetzt',
                     cssclass:  'tv_jetzt'
                   },
    tipps:         { feedname: 'Tipps',
                     url: 'http://www.tvspielfilm.de/tv-programm/rss/tipps.xml',
                     state: 'rss.tipps',
                     cssclass:  'tv_tipps'
                    },
    heute2015uhr:  { feedname: 'heute 20:15 Uhr',
                     url: 'http://www.tvspielfilm.de/tv-programm/rss/heute2015.xml',
                     state: 'rss.heute2015',
                     cssclass:  'tv_heute2015'
                    },
    heute2200uhr:  { feedname: 'heute 22:00 Uhr',
                     url: 'http://www.tvspielfilm.de/tv-programm/rss/heute2200.xml',
                     state: 'rss.heute2200',
                     cssclass:  'tv_heute2200'
                    },
    filme:          { feedname: 'Spielfilm-Highlights des Tages',
                     url: 'http://www.tvspielfilm.de/tv-programm/rss/filme.xml',
                     state: 'rss.filme',
                     cssclass:  'tv_filme'
                    }
}

function readFeed (x) {
    var link = rss_options[x].url;
    adapter.log.info('RSS Feed wird eingelesen: ' + link);
    request(link, function (error, response, body) {
        if (!error && response.statusCode == 200) {

            parseString(body, {
                explicitArray: false,
                mergeAttrs: true
            }, function (err, result) {
                var data = JSON.stringify(result, null, 2);
                var table = [];
                if (err) {
                    adapter.log.warn("Fehler: " + err);
                } else {
                    var sender_empfangbar = false;
                    if (result.rss.channel.item.length !== null) { // gelegentlicher Fehler bei nächtlicher Abfrage durch length (undefined) soll hier abgefangen werden
                        // Array durchzaehlen von 0 bis Zahl der items
                        for(var i = 0; i < result.rss.channel.item.length; i++) {
                            sender_empfangbar = check_sender(result.rss.channel.item[i].title);
                            if (sender_empfangbar) {
                                var entry = {
                                    image: result.rss.channel.item[i].enclosure ? '' : '',
                                    text:  '

' + result.rss.channel.item[i].title +
                                       '

' +
                                       result.rss.channel.item[i].description +'

',
                                    _Bild: result.rss.channel.item[i].enclosure ? '' : 'no image'
                                };
                                table.push(entry);
                            } // Ende Abfrage, ob Sender empfangbar
                        }
                    } else adapter.log.warn('LENGTH in TV Programm (' + rss_options[x].feedname + ') nicht definiert'); // ende if ungleich
                }
                adapter.setState(rss_options[x].state, {val: JSON.stringify(table), ack: true});// ganze XML in Objekt für Table Widget
            });
        } else adapter.log.warn(error,'error');
    });   // Ende request
    adapter.log.info('XML-Daten aus TV Spielfilm (' + rss_options[x].feedname + ') eingelesen');
}
function main() {
    readSettings();
    for (var j in rss_options) {
        readFeed(j);
    }
    adapter.stop();
}
<|endoftext|>


<commit_before>
const history  = window.history;
const location = window.location;
const nothing  = Object.freeze({});

function PopstateHandler(fn, context) {
    this.context = context || null;
    this.fn = fn;
}

PopstateHandler.prototype.handleEvent = function handleEvent(e) {
    return this.fn.call(this.context, new URL(location.href), e.state);
};

export default {
    /** .href **/
    get href() {
        return location.href;
    },

    /** .state **/
    get state() {
        return history.state;
    },

    set state(state) {
        history.replaceState(state, document.title);
    },

    navigateTo: function(url) {
        console.log('navigateTo', url);
    },

    /** .navigations(fn) **/
    navigations: function(fn) {
        // Fires fn(url, state) on popstate
        window.addEventListener('popstate', new PopstateHandler(fn));
    },

    /** .push(url, state) **/
    push: function push(url, state) {
        state = state || nothing;
        history.pushState(state, document.title, url);
        return this;
    },

    /** .replace(url, state) **/
    replace: function replace(url, state) {
        state = state || nothing;
        history.replaceState(state, document.title, url);
        return this;
    },

    /** .url() **/
    url: function url() {
        return new URL(location.href);
    }
};

window.addEventListener('hashchange', function(e) {
    // Detect navigations to # and silently remove the # from the url
    if (location.hash === '') {
        history.replaceState(history.state, document.title, location.href.replace(/#$/, ''));
    }
});
<commit_msg>History<commit_after>
const history  = window.history;
const location = window.location;
const nothing  = Object.freeze({});

import create from './create.js';
import delegate from './delegate.js';

const dummyId   = 'dummy-target-for-managing-scroll';
const dummyElem = create('div', {
    id: 'dummy-target-for-managing-scroll',
    style: 'position: fixed; top: 0;'
});

//document.body.appendChild(dummy);

function PopstateHandler(fn, context) {
    this.context = context || null;
    this.fn = fn;
}

PopstateHandler.prototype.handleEvent = function handleEvent(e) {
    return this.fn.call(this.context, new URL(location.href), e.state);
};

function stripHash(hash) {
    return hash.replace(/^#/, '');
}

const lll = {
    /** .id **/
    get id() {
        return stripHash(location.hash);
    },

    set id(id) {
        // Replace the hash without changing history
        history.replaceState(history.state, document.title, id ?
            '#' + id :
            location.href.replace(/#.*$/, '')
        );
    },

    /** .url **/
    get url() {
        return location.href;
    },

    set url(url) {
        // Replace the url without changing history
        history.replaceState(history.state, document.title, url);
    },

    /** .state **/
    get state() {
        return history.state;
    },

    set state(state) {
        // Replace state without changing history
        history.replaceState(state, document.title);
    },

    /** .navigate(url) **/
    navigate: function(url, scroll = true) {
        console.log('navigate()', url);

        const id = (/#(.*)$/.exec(url) || [])[1];
        if (id === undefined) { return; }

        // If we navigate to empty hash, prevent scroll-to-top by navigating to
        // a dummy element with position: fixed, and immediately reset the hash
        // (via replaceState so that it does not cause a second hashchange).
        // Conveniently, the hashchange event fires after the replacement and
        // the hash has already been emptied.
        if (id === '') {
            // Target dummy to avoid scroll
            const id = dummyId;
            document.body.appendChild(dummyElem);
            location.hash = id;
            history.replaceState(history.state, document.title, location.href.replace(/#.*$/, ''));
            dummyElem.remove();
            return;
        }

        //const target = document.getElementById(id);
        //target.id = '';
        location.hash = id;
        //target.id = id;
    },

    /** .navigations(fn) **/
    navigations: function(fn) {
        // Fires fn(url, state) on popstate
        window.addEventListener('popstate', new PopstateHandler(fn));
    },

    /** .push(url, state) **/
    push: function push(url, state) {
        state = state || nothing;
        history.pushState(state, document.title, url);
        return this;
    },

    /** .replace(url, state) **/
    replace: function replace(url, state) {
        state = state || nothing;
        history.replaceState(state, document.title, url);
        return this;
    },

    /** .url() **/
    URL: function url() {
        return new URL(location.href);
    }
};

export default lll;

document.addEventListener('click', delegate({
    '[href*="#"]': function(link, e) {
        // Grab links pointing inside this documnebnt
        if (link.origin !== window.location.origin) { return; }
        lll.navigate(link.href);
        e.preventDefault();
    }
}));

window.addEventListener('hashchange', function(e) {
    console.log('HASHCHANGE', location.href);
    // Detect navigations to # and silently remove the # from the url
    if (stripHash(location.hash) === '') {
        history.replaceState(history.state, document.title, location.href.replace(/#$/, ''));
    }
});

window.addEventListener('scroll', function(e) {
    console.log('SCROLL', location.href);
});
<|endoftext|>


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

const utils = require('./utils');

module.exports.addTests = function({testRunner, expect}) {
  const {describe, xdescribe, fdescribe} = testRunner;
  const {it, fit, xit} = testRunner;
  const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;

  describe('ElementHandle.boundingBox', function() {
    it('should work', async({page, server}) => {
      await page.setViewport({width: 500, height: 500});
      await page.goto(server.PREFIX + '/grid.html');
      const elementHandle = await page.$('.box:nth-of-type(13)');
      const box = await elementHandle.boundingBox();
      expect(box).toEqual({ x: 100, y: 50, width: 50, height: 50 });
    });
    it('should handle nested frames', async({page, server}) => {
      await page.setViewport({width: 500, height: 500});
      await page.goto(server.PREFIX + '/frames/nested-frames.html');
      const nestedFrame = page.frames()[1].childFrames()[1];
      const elementHandle = await nestedFrame.$('div');
      const box = await elementHandle.boundingBox();
      expect(box).toEqual({ x: 28, y: 260, width: 264, height: 18 });
    });
    it('should return null for invisible elements', async({page, server}) => {
      await page.setContent('
hi
');
      const element = await page.$('div');
      expect(await element.boundingBox()).toBe(null);
    });
    it('should force a layout', async({page, server}) => {
      await page.setViewport({ width: 500, height: 500 });
      await page.setContent('
hello
');
      const elementHandle = await page.$('div');
      await page.evaluate(element => element.style.height = '200px', elementHandle);
      const box = await elementHandle.boundingBox();
      expect(box).toEqual({ x: 8, y: 8, width: 100, height: 200 });
    });
  });

  describe('ElementHandle.boxModel', function() {
    it('should work', async({page, server}) => {
      await page.goto(server.PREFIX + '/resetcss.html');

      // Step 1: Add Frame and position it absolutely.
      await utils.attachFrame(page, 'frame1', server.PREFIX + '/resetcss.html');
      await page.evaluate(() => {
        const frame = document.querySelector('#frame1');
        frame.style = `
          position: absolute;
          left: 1px;
          top: 2px;
        `;
      });

      // Step 2: Add div and position it absolutely inside frame.
      const frame = page.frames()[1];
      const divHandle = (await frame.evaluateHandle(() => {
        const div = document.createElement('div');
        document.body.appendChild(div);
        div.style = `
          box-sizing: border-box;
          position: absolute;
          border-left: 1px solid black;
          padding-left: 2px;
          margin-left: 3px;
          left: 4px;
          top: 5px;
          width: 6px;
          height: 7px;
        `;
        return div;
      })).asElement();

      // Step 3: query div's boxModel and assert box values.
      const box = await divHandle.boxModel();
      expect(box.width).toBe(6);
      expect(box.height).toBe(7);
      expect(box.margin[0]).toEqual({
        x: 1 + 4, // frame.left + div.left
        y: 2 + 5,
      });
      expect(box.border[0]).toEqual({
        x: 1 + 4 + 3, // frame.left + div.left + div.margin-left
        y: 2 + 5,
      });
      expect(box.padding[0]).toEqual({
        x: 1 + 4 + 3 + 1, // frame.left + div.left + div.marginLeft + div.borderLeft
        y: 2 + 5,
      });
      expect(box.content[0]).toEqual({
        x: 1 + 4 + 3 + 1 + 2, // frame.left + div.left + div.marginLeft + div.borderLeft + dif.paddingLeft
        y: 2 + 5,
      });
    });

    it('should return null for invisible elements', async({page, server}) => {
      await page.setContent('
hi
');
      const element = await page.$('div');
      expect(await element.boxModel()).toBe(null);
    });
  });

  describe('ElementHandle.contentFrame', function() {
    it('should work', async({page,server}) => {
      await page.goto(server.EMPTY_PAGE);
      await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
      const elementHandle = await page.$('#frame1');
      const frame = await elementHandle.contentFrame();
      expect(frame).toBe(page.frames()[1]);
    });
  });

  describe('ElementHandle.click', function() {
    it('should work', async({page, server}) => {
      await page.goto(server.PREFIX + '/input/button.html');
      const button = await page.$('button');
      await button.click();
      expect(await page.evaluate(() => result)).toBe('Clicked');
    });
    it('should work for Shadow DOM v1', async({page, server}) => {
      await page.goto(server.PREFIX + '/shadow.html');
      const buttonHandle = await page.evaluateHandle(() => button);
      await buttonHandle.click();
      expect(await page.evaluate(() => clicked)).toBe(true);
    });
    it('should work for TextNodes', async({page, server}) => {
      await page.goto(server.PREFIX + '/input/button.html');
      const buttonTextNode = await page.evaluateHandle(() => document.querySelector('button').firstChild);
      let error = null;
      await buttonTextNode.click().catch(err => error = err);
      expect(error.message).toBe('Node is not of type HTMLElement');
    });
    it('should throw for detached nodes', async({page, server}) => {
      await page.goto(server.PREFIX + '/input/button.html');
      const button = await page.$('button');
      await page.evaluate(button => button.remove(), button);
      let error = null;
      await button.click().catch(err => error = err);
      expect(error.message).toBe('Node is detached from document');
    });
    it('should throw for hidden nodes', async({page, server}) => {
      await page.goto(server.PREFIX + '/input/button.html');
      const button = await page.$('button');
      await page.evaluate(button => button.style.display = 'none', button);
      const error = await button.click().catch(err => err);
      expect(error.message).toBe('Node is either not visible or not an HTMLElement');
    });
    it('should throw for recursively hidden nodes', async({page, server}) => {
      await page.goto(server.PREFIX + '/input/button.html');
      const button = await page.$('button');
      await page.evaluate(button => button.parentElement.style.display = 'none', button);
      const error = await button.click().catch(err => err);
      expect(error.message).toBe('Node is either not visible or not an HTMLElement');
    });
    it('should throw for
 elements', async({page, server}) => {
      await page.setContent('hello
goodbye');
      const br = await page.$('br');
      const error = await br.click().catch(err => err);
      expect(error.message).toBe('Node is either not visible or not an HTMLElement');
    });
  });

  describe('ElementHandle.hover', function() {
    it('should work', async({page, server}) => {
      await page.goto(server.PREFIX + '/input/scrollable.html');
      const button = await page.$('#button-6');
      await button.hover();
      expect(await page.evaluate(() => document.querySelector('button:hover').id)).toBe('button-6');
    });
  });

  describe('ElementHandle.screenshot', function() {
    it('should work', async({page, server}) => {
      await page.setViewport({width: 500, height: 500});
      await page.goto(server.PREFIX + '/grid.html');
      await page.evaluate(() => window.scrollBy(50, 100));
      const elementHandle = await page.$('.box:nth-of-type(3)');
      const screenshot = await elementHandle.screenshot();
      expect(screenshot).toBeGolden('screenshot-element-bounding-box.png');
    });
    it('should take into account padding and border', async({page, server}) => {
      await page.setViewport({width: 500, height: 500});
      await page.setContent(`
        something above

      `);
      const elementHandle = await page.$('div');
      const screenshot = await elementHandle.screenshot();
      expect(screenshot).toBeGolden('screenshot-element-padding-border.png');
    });
    it('should capture full element when larger than viewport', async({page, server}) => {
      await page.setViewport({width: 500, height: 500});

      await page.setContent(`
        something above

      `);
      const elementHandle = await page.$('div.to-screenshot');
      const screenshot = await elementHandle.screenshot();
      expect(screenshot).toBeGolden('screenshot-element-larger-than-viewport.png');

      expect(await page.evaluate(() => ({ w: window.innerWidth, h: window.innerHeight }))).toEqual({ w: 500, h: 500 });
    });
    it('should scroll element into view', async({page, server}) => {
      await page.setViewport({width: 500, height: 500});
      await page.setContent(`
        something above

      `);
      const elementHandle = await page.$('div.to-screenshot');
      const screenshot = await elementHandle.screenshot();
      expect(screenshot).toBeGolden('screenshot-element-scrolled-into-view.png');
    });
    it('should work with a rotated element', async({page, server}) => {
      await page.setViewport({width: 500, height: 500});
      await page.setContent(`

`);
      const elementHandle = await page.$('div');
      const screenshot = await elementHandle.screenshot();
      expect(screenshot).toBeGolden('screenshot-element-rotate.png');
    });
    it('should fail to screenshot a detached element', async({page, server}) => {
      await page.setContent('
remove this
');
      const elementHandle = await page.$('h1');
      await page.evaluate(element => element.remove(), elementHandle);
      const screenshotError = await elementHandle.screenshot().catch(error => error);
      expect(screenshotError.message).toBe('Node is either not visible or not an HTMLElement');
    });
  });

  describe('ElementHandle.$', function() {
    it('should query existing element', async({page, server}) => {
      await page.goto(server.PREFIX + '/playground.html');
      await page.setContent('

A

');
      const html = await page.$('html');
      const second = await html.$('.second');
      const inner = await second.$('.inner');
      const content = await page.evaluate(e => e.textContent, inner);
      expect(content).toBe('A');
    });

    it('should return null for non-existing element', async({page, server}) => {
      await page.setContent('

B

');
      const html = await page.$('html');
      const second = await html.$('.third');
      expect(second).toBe(null);
    });
  });
  describe('ElementHandle.$eval', function() {
    it('should work', async({page, server}) => {
      await page.setContent('

100

10

');
      const tweet = await page.$('.tweet');
      const content = await tweet.$eval('.like', node => node.innerText);
      expect(content).toBe('100');
    });

    it('should retrieve content from subtree', async({page, server}) => {
      const htmlContent = '
not-a-child-div

a-child-div

';
      await page.setContent(htmlContent);
      const elementHandle = await page.$('#myId');
      const content = await elementHandle.$eval('.a', node => node.innerText);
      expect(content).toBe('a-child-div');
    });
  });

  describe('ElementHandle.$$', function() {
    it('should query existing elements', async({page, server}) => {
      await page.setContent('
A

B
');
      const html = await page.$('html');
      const elements = await html.$$('div');
      expect(elements.length).toBe(2);
      const promises = elements.map(element => page.evaluate(e => e.textContent, element));
      expect(await Promise.all(promises)).toEqual(['A', 'B']);
    });

    it('should return empty array for non-existing elements', async({page, server}) => {
      await page.setContent('A
B');
      const html = await page.$('html');
      const elements = await html.$$('div');
      expect(elements.length).toBe(0);
    });
  });

  describe('ElementHandle.$x', function() {
    it('should query existing element', async({page, server}) => {
      await page.goto(server.PREFIX + '/playground.html');
      await page.setContent('

A

');
      const html = await page.$('html');
      const second = await html.$x(`./body/div[contains(@class, 'second')]`);
      const inner = await second[0].$x(`./div[contains(@class, 'inner')]`);
      const content = await page.evaluate(e => e.textContent, inner[0]);
      expect(content).toBe('A');
    });

    it('should return null for non-existing element', async({page, server}) => {
      await page.setContent('

B

');
      const html = await page.$('html');
      const second = await html.$x(`/div[contains(@class, 'third')]`);
      expect(second).toEqual([]);
    });
  });
};
<commit_msg>test: add a missing test for elementHandle. (#2580)<commit_after>/**
 * Copyright 2018 Google Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

const utils = require('./utils');

module.exports.addTests = function({testRunner, expect}) {
  const {describe, xdescribe, fdescribe} = testRunner;
  const {it, fit, xit} = testRunner;
  const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;

  describe('ElementHandle.boundingBox', function() {
    it('should work', async({page, server}) => {
      await page.setViewport({width: 500, height: 500});
      await page.goto(server.PREFIX + '/grid.html');
      const elementHandle = await page.$('.box:nth-of-type(13)');
      const box = await elementHandle.boundingBox();
      expect(box).toEqual({ x: 100, y: 50, width: 50, height: 50 });
    });
    it('should handle nested frames', async({page, server}) => {
      await page.setViewport({width: 500, height: 500});
      await page.goto(server.PREFIX + '/frames/nested-frames.html');
      const nestedFrame = page.frames()[1].childFrames()[1];
      const elementHandle = await nestedFrame.$('div');
      const box = await elementHandle.boundingBox();
      expect(box).toEqual({ x: 28, y: 260, width: 264, height: 18 });
    });
    it('should return null for invisible elements', async({page, server}) => {
      await page.setContent('
hi
');
      const element = await page.$('div');
      expect(await element.boundingBox()).toBe(null);
    });
    it('should force a layout', async({page, server}) => {
      await page.setViewport({ width: 500, height: 500 });
      await page.setContent('
hello
');
      const elementHandle = await page.$('div');
      await page.evaluate(element => element.style.height = '200px', elementHandle);
      const box = await elementHandle.boundingBox();
      expect(box).toEqual({ x: 8, y: 8, width: 100, height: 200 });
    });
  });

  describe('ElementHandle.boxModel', function() {
    it('should work', async({page, server}) => {
      await page.goto(server.PREFIX + '/resetcss.html');

      // Step 1: Add Frame and position it absolutely.
      await utils.attachFrame(page, 'frame1', server.PREFIX + '/resetcss.html');
      await page.evaluate(() => {
        const frame = document.querySelector('#frame1');
        frame.style = `
          position: absolute;
          left: 1px;
          top: 2px;
        `;
      });

      // Step 2: Add div and position it absolutely inside frame.
      const frame = page.frames()[1];
      const divHandle = (await frame.evaluateHandle(() => {
        const div = document.createElement('div');
        document.body.appendChild(div);
        div.style = `
          box-sizing: border-box;
          position: absolute;
          border-left: 1px solid black;
          padding-left: 2px;
          margin-left: 3px;
          left: 4px;
          top: 5px;
          width: 6px;
          height: 7px;
        `;
        return div;
      })).asElement();

      // Step 3: query div's boxModel and assert box values.
      const box = await divHandle.boxModel();
      expect(box.width).toBe(6);
      expect(box.height).toBe(7);
      expect(box.margin[0]).toEqual({
        x: 1 + 4, // frame.left + div.left
        y: 2 + 5,
      });
      expect(box.border[0]).toEqual({
        x: 1 + 4 + 3, // frame.left + div.left + div.margin-left
        y: 2 + 5,
      });
      expect(box.padding[0]).toEqual({
        x: 1 + 4 + 3 + 1, // frame.left + div.left + div.marginLeft + div.borderLeft
        y: 2 + 5,
      });
      expect(box.content[0]).toEqual({
        x: 1 + 4 + 3 + 1 + 2, // frame.left + div.left + div.marginLeft + div.borderLeft + dif.paddingLeft
        y: 2 + 5,
      });
    });

    it('should return null for invisible elements', async({page, server}) => {
      await page.setContent('
hi
');
      const element = await page.$('div');
      expect(await element.boxModel()).toBe(null);
    });
  });

  describe('ElementHandle.contentFrame', function() {
    it('should work', async({page,server}) => {
      await page.goto(server.EMPTY_PAGE);
      await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
      const elementHandle = await page.$('#frame1');
      const frame = await elementHandle.contentFrame();
      expect(frame).toBe(page.frames()[1]);
    });
  });

  describe('ElementHandle.click', function() {
    it('should work', async({page, server}) => {
      await page.goto(server.PREFIX + '/input/button.html');
      const button = await page.$('button');
      await button.click();
      expect(await page.evaluate(() => result)).toBe('Clicked');
    });
    it('should work for Shadow DOM v1', async({page, server}) => {
      await page.goto(server.PREFIX + '/shadow.html');
      const buttonHandle = await page.evaluateHandle(() => button);
      await buttonHandle.click();
      expect(await page.evaluate(() => clicked)).toBe(true);
    });
    it('should work for TextNodes', async({page, server}) => {
      await page.goto(server.PREFIX + '/input/button.html');
      const buttonTextNode = await page.evaluateHandle(() => document.querySelector('button').firstChild);
      let error = null;
      await buttonTextNode.click().catch(err => error = err);
      expect(error.message).toBe('Node is not of type HTMLElement');
    });
    it('should throw for detached nodes', async({page, server}) => {
      await page.goto(server.PREFIX + '/input/button.html');
      const button = await page.$('button');
      await page.evaluate(button => button.remove(), button);
      let error = null;
      await button.click().catch(err => error = err);
      expect(error.message).toBe('Node is detached from document');
    });
    it('should throw for hidden nodes', async({page, server}) => {
      await page.goto(server.PREFIX + '/input/button.html');
      const button = await page.$('button');
      await page.evaluate(button => button.style.display = 'none', button);
      const error = await button.click().catch(err => err);
      expect(error.message).toBe('Node is either not visible or not an HTMLElement');
    });
    it('should throw for recursively hidden nodes', async({page, server}) => {
      await page.goto(server.PREFIX + '/input/button.html');
      const button = await page.$('button');
      await page.evaluate(button => button.parentElement.style.display = 'none', button);
      const error = await button.click().catch(err => err);
      expect(error.message).toBe('Node is either not visible or not an HTMLElement');
    });
    it('should throw for
 elements', async({page, server}) => {
      await page.setContent('hello
goodbye');
      const br = await page.$('br');
      const error = await br.click().catch(err => err);
      expect(error.message).toBe('Node is either not visible or not an HTMLElement');
    });
  });

  describe('ElementHandle.hover', function() {
    it('should work', async({page, server}) => {
      await page.goto(server.PREFIX + '/input/scrollable.html');
      const button = await page.$('#button-6');
      await button.hover();
      expect(await page.evaluate(() => document.querySelector('button:hover').id)).toBe('button-6');
    });
  });

  describe('ElementHandle.screenshot', function() {
    it('should work', async({page, server}) => {
      await page.setViewport({width: 500, height: 500});
      await page.goto(server.PREFIX + '/grid.html');
      await page.evaluate(() => window.scrollBy(50, 100));
      const elementHandle = await page.$('.box:nth-of-type(3)');
      const screenshot = await elementHandle.screenshot();
      expect(screenshot).toBeGolden('screenshot-element-bounding-box.png');
    });
    it('should take into account padding and border', async({page, server}) => {
      await page.setViewport({width: 500, height: 500});
      await page.setContent(`
        something above

      `);
      const elementHandle = await page.$('div');
      const screenshot = await elementHandle.screenshot();
      expect(screenshot).toBeGolden('screenshot-element-padding-border.png');
    });
    it('should capture full element when larger than viewport', async({page, server}) => {
      await page.setViewport({width: 500, height: 500});

      await page.setContent(`
        something above

      `);
      const elementHandle = await page.$('div.to-screenshot');
      const screenshot = await elementHandle.screenshot();
      expect(screenshot).toBeGolden('screenshot-element-larger-than-viewport.png');

      expect(await page.evaluate(() => ({ w: window.innerWidth, h: window.innerHeight }))).toEqual({ w: 500, h: 500 });
    });
    it('should scroll element into view', async({page, server}) => {
      await page.setViewport({width: 500, height: 500});
      await page.setContent(`
        something above

      `);
      const elementHandle = await page.$('div.to-screenshot');
      const screenshot = await elementHandle.screenshot();
      expect(screenshot).toBeGolden('screenshot-element-scrolled-into-view.png');
    });
    it('should work with a rotated element', async({page, server}) => {
      await page.setViewport({width: 500, height: 500});
      await page.setContent(`

`);
      const elementHandle = await page.$('div');
      const screenshot = await elementHandle.screenshot();
      expect(screenshot).toBeGolden('screenshot-element-rotate.png');
    });
    it('should fail to screenshot a detached element', async({page, server}) => {
      await page.setContent('
remove this
');
      const elementHandle = await page.$('h1');
      await page.evaluate(element => element.remove(), elementHandle);
      const screenshotError = await elementHandle.screenshot().catch(error => error);
      expect(screenshotError.message).toBe('Node is either not visible or not an HTMLElement');
    });
  });

  describe('ElementHandle.$', function() {
    it('should query existing element', async({page, server}) => {
      await page.goto(server.PREFIX + '/playground.html');
      await page.setContent('

A

');
      const html = await page.$('html');
      const second = await html.$('.second');
      const inner = await second.$('.inner');
      const content = await page.evaluate(e => e.textContent, inner);
      expect(content).toBe('A');
    });

    it('should return null for non-existing element', async({page, server}) => {
      await page.setContent('

B

');
      const html = await page.$('html');
      const second = await html.$('.third');
      expect(second).toBe(null);
    });
  });
  describe('ElementHandle.$eval', function() {
    it('should work', async({page, server}) => {
      await page.setContent('

100

10

');
      const tweet = await page.$('.tweet');
      const content = await tweet.$eval('.like', node => node.innerText);
      expect(content).toBe('100');
    });

    it('should retrieve content from subtree', async({page, server}) => {
      const htmlContent = '
not-a-child-div

a-child-div

';
      await page.setContent(htmlContent);
      const elementHandle = await page.$('#myId');
      const content = await elementHandle.$eval('.a', node => node.innerText);
      expect(content).toBe('a-child-div');
    });

    it('should throw in case of missing selector', async({page, server}) => {
      const htmlContent = '
not-a-child-div

';
      await page.setContent(htmlContent);
      const elementHandle = await page.$('#myId');
      const errorMessage = await elementHandle.$eval('.a', node => node.innerText).catch(error => error.message);
      expect(errorMessage).toBe(`Error: failed to find element matching selector ".a"`);
    });
  });

  describe('ElementHandle.$$', function() {
    it('should query existing elements', async({page, server}) => {
      await page.setContent('
A

B
');
      const html = await page.$('html');
      const elements = await html.$$('div');
      expect(elements.length).toBe(2);
      const promises = elements.map(element => page.evaluate(e => e.textContent, element));
      expect(await Promise.all(promises)).toEqual(['A', 'B']);
    });

    it('should return empty array for non-existing elements', async({page, server}) => {
      await page.setContent('A
B');
      const html = await page.$('html');
      const elements = await html.$$('div');
      expect(elements.length).toBe(0);
    });
  });

  describe('ElementHandle.$x', function() {
    it('should query existing element', async({page, server}) => {
      await page.goto(server.PREFIX + '/playground.html');
      await page.setContent('

A

');
      const html = await page.$('html');
      const second = await html.$x(`./body/div[contains(@class, 'second')]`);
      const inner = await second[0].$x(`./div[contains(@class, 'inner')]`);
      const content = await page.evaluate(e => e.textContent, inner[0]);
      expect(content).toBe('A');
    });

    it('should return null for non-existing element', async({page, server}) => {
      await page.setContent('

B

');
      const html = await page.$('html');
      const second = await html.$x(`/div[contains(@class, 'third')]`);
      expect(second).toEqual([]);
    });
  });
};
<|endoftext|>


<commit_before>const electron = require('electron');
const app = electron.app;
const Menu = electron.Menu;
const Tray = electron.Tray;
const spawn = require('child_process').spawn;

const capture = function() {
  spawn('screencapture', ['-i', 'screencapture.png'])
};

var appIcon = null;

app.on('ready', function(){
  appIcon = new Tray('./images/camera.png');
  var contextMenu = Menu.buildFromTemplate([
    { label: 'Capture', click: function() { capture(); } },
    { type: 'separator' },
    { label: 'Quit', accelerator: 'Command+Q', click: function() { app.quit(); } }
  ]);
  appIcon.setToolTip('Cammy');
  appIcon.setContextMenu(contextMenu);
});
<commit_msg>Add unixtimestamp<commit_after>const electron = require('electron');
const app = electron.app;
const Menu = electron.Menu;
const Tray = electron.Tray;
const spawn = require('child_process').spawn;

const capture = function() {
  const unixtimestamp = parseInt((new Date) / 1000);
  const filePath = `${unixtimestamp}.png`;
  spawn('screencapture', ['-i', filePath]);
};

var appIcon = null;

app.on('ready', function(){
  appIcon = new Tray('./images/camera.png');
  var contextMenu = Menu.buildFromTemplate([
    { label: 'Capture', click: function() { capture(); } },
    { type: 'separator' },
    { label: 'Quit', accelerator: 'Command+Q', click: function() { app.quit(); } }
  ]);
  appIcon.setToolTip('Cammy');
  appIcon.setContextMenu(contextMenu);
});
<|endoftext|>