<commit_before>import Globalize from "globalize";
import React from "react";
import generator from "./generator";
import "globalize/message";
import "globalize/plural";

function messageSetup(globalize, props, globalizePropValues) {
    var defaultMessage;
    var children = props.children;
    var scope = props.scope;

    function getDefaultMessage(children) {
        if (typeof children === "string") {
            return children;
        } else {
            throw new Error("Invalid default message type `" + typeof children + "`");
        }
    }

    // Set path - path as props supercedes default value.
    if (props.path) {
        // Override generator assumption. The generator assumes the globalizePropValues[0]
        // (path) and props.children to be mutually exclusive, but this isn't
        // true here for messages. Because, it's possible to use props.path (for
        // path) and props.children for defaultMessage, which are two different
        // variables.
        globalizePropValues[0] = props.path;
    } else {
        // Although the generator had already set globalizePropValues[0] (path) as
        // props.children, here its type is checked and its value is sanitized.
        defaultMessage = getDefaultMessage(children);
        globalizePropValues[0] = sanitizePath(defaultMessage);
    }

    // Scope path.
    if (scope) {
        globalizePropValues[0] = scope + "/" + globalizePropValues[0];
    }

    // Development mode only.
    if (process.env.NODE_ENV !== "production") {
        var path = props.path ? props.path.split("/") : [globalizePropValues[0]];
        /* eslint-disable no-inner-declarations */
        function getMessage(globalize, path) {
            return globalize.cldr.get(["globalize-messages/{bundle}"].concat(path));
        }

        function setMessage(globalize, path, message) {
            var data = {};
            function set(data, path, value) {
                var i;
                var node = data;
                var length = path.length;

                for (i = 0; i < length - 1; i++) {
                    if (!node[path[i]]) {
                        node[path[i]] = {};
                    }
                    node = node[path[i]];
                }
                node[path[i]] = value;
            }
            set(data, [globalize.cldr.attributes.bundle].concat(path), message);
            Globalize.loadMessages(data);
        }
        /* eslint-enable no-inner-declarations */

        if (globalize.cldr) {
            if (!getMessage(globalize, path)) {
                defaultMessage = defaultMessage || getDefaultMessage(children);
                setMessage(globalize, path, defaultMessage);
            }
        }
    }
}

function replaceElements(props, formatted) {
    var elements = props.elements;

    function _replaceElements(string, elements) {
        if (typeof string !== "string") {
            throw new Error("Missing or invalid string `" + string + "` (" + typeof string + ")");
        }
        if (typeof elements !== "object") {
            throw new Error("Missing or invalid elements `" + elements + "` (" + typeof elements + ")");
        }

        // Given [x, y, z], it returns [x, element, y, element, z].
        function spreadElementsInBetweenItems(array, element) {
            var getElement = typeof element === "function" ? element : function() {
                return element;
            };
            return array.slice(1).reduce(function(ret, item, i) {
                ret.push(getElement(i), item);
                return ret;
            }, [array[0]]);
        }

        function splice(sourceArray, start, deleteCount, itemsArray) {
            [].splice.apply(sourceArray, [start, deleteCount].concat(itemsArray));
        }

        return Object.keys(elements).reduce(function(ret, key) {
            var element = elements[key];

            ret.forEach(function(string, i) {
                var aux, contents, regexp, regexp2;

                // Insert array into the correct ret position.
                function replaceRetItem(array) {
                    splice(ret, i, 1, array);
                }

                if (typeof string !== "string") {
                    return; // continue;
                }

                // Empty tags, e.g., `[foo/]`.
                aux = string.split("[" + key + "/]");
                if (aux.length > 1) {
                    aux = spreadElementsInBetweenItems(aux, element);
                    replaceRetItem(aux);
                    return; // continue;
                }

                // Start-end tags, e.g., `[foo]content[/foo]`.
                regexp = new RegExp("\\[" + key + "\\][\\s\\S]*?\\[\\/" + key + "\\]", "g");
                regexp2 = new RegExp("\\[" + key + "\\]([\\s\\S]*?)\\[\\/" + key + "\\]");
                aux = string.split(regexp);
                if (aux.length > 1) {
                    contents = string.match(regexp).map(function(content) {
                        return content.replace(regexp2, "$1");
                    });
                    aux = spreadElementsInBetweenItems(aux, function(i) {
                        return React.cloneElement(element, {}, contents[i]);
                    });
                    replaceRetItem(aux);
                }
            });

            return ret;
        }, [string]);
    }

    // Elements replacement.
    if (elements) {
        formatted = React.DOM.span.apply(React.DOM.span, [{}].concat(_replaceElements(formatted, elements)));
    }

    return formatted;
}

function sanitizePath(pathString) {
    return pathString.trim().replace(/\{/g, "(").replace(/\}/g, ")").replace(/\//g, "|").replace(/\n/g, " ").replace(/ +/g, " ").replace(/"/g, "'");
}

// Overload Globalize's `.formatMessage` to allow default message.
var globalizeMessageFormatter = Globalize.messageFormatter;
Globalize.messageFormatter = Globalize.prototype.messageFormatter = function(pathOrMessage) {
    var aux = {};
    var sanitizedPath = sanitizePath(pathOrMessage);

    // Globalize runtime
    if (!this.cldr) {
        // On runtime, the only way for deciding between using sanitizedPath or
        // pathOrMessage as path is by checking which formatter exists.
        arguments[0] = sanitizedPath;
        aux = globalizeMessageFormatter.apply(this, arguments);
        arguments[0] = pathOrMessage;
        return aux || globalizeMessageFormatter.apply(this, arguments);
    }

    var sanitizedPathExists = this.cldr.get(["globalize-messages/{bundle}", sanitizedPath]) !== undefined;
    var pathExists = this.cldr.get(["globalize-messages/{bundle}", pathOrMessage]) !== undefined;

    // Want to distinguish between default message and path value - just checking
    // for sanitizedPath won't be enough, because sanitizedPath !== pathOrMessage
    // for paths like "salutations/hi".
    if (!sanitizedPathExists && !pathExists) {
        aux[this.cldr.attributes.bundle] = {};
        aux[this.cldr.attributes.bundle][sanitizedPath] = pathOrMessage;
        Globalize.loadMessages(aux);
        sanitizedPathExists = true;
    }

    arguments[0] = sanitizedPathExists ? sanitizedPath : pathOrMessage;
    return globalizeMessageFormatter.apply(this, arguments);
};

export default generator("formatMessage", ["path", "variables"], {
    beforeFormat: function(props) {
        messageSetup(this.globalize, props, this.globalizePropValues);
    },
    afterFormat: function(formattedValue) {
        return replaceElements(this.props, formattedValue);
    }
});
<commit_msg>Fix "Function declarations should not be placed in blocks"<commit_after>import Globalize from "globalize";
import React from "react";
import generator from "./generator";
import "globalize/message";
import "globalize/plural";

function messageSetup(globalize, props, globalizePropValues) {
    var defaultMessage;
    var children = props.children;
    var scope = props.scope;

    function getDefaultMessage(children) {
        if (typeof children === "string") {
            return children;
        } else {
            throw new Error("Invalid default message type `" + typeof children + "`");
        }
    }

    // Set path - path as props supercedes default value.
    if (props.path) {
        // Override generator assumption. The generator assumes the globalizePropValues[0]
        // (path) and props.children to be mutually exclusive, but this isn't
        // true here for messages. Because, it's possible to use props.path (for
        // path) and props.children for defaultMessage, which are two different
        // variables.
        globalizePropValues[0] = props.path;
    } else {
        // Although the generator had already set globalizePropValues[0] (path) as
        // props.children, here its type is checked and its value is sanitized.
        defaultMessage = getDefaultMessage(children);
        globalizePropValues[0] = sanitizePath(defaultMessage);
    }

    // Scope path.
    if (scope) {
        globalizePropValues[0] = scope + "/" + globalizePropValues[0];
    }

    // Development mode only.
    if (process.env.NODE_ENV !== "production") {
        var path = props.path ? props.path.split("/") : [globalizePropValues[0]];
        var getMessage = function(globalize, path) {
            return globalize.cldr.get(["globalize-messages/{bundle}"].concat(path));
        };

        var setMessage = function(globalize, path, message) {
            var data = {};
            function set(data, path, value) {
                var i;
                var node = data;
                var length = path.length;

                for (i = 0; i < length - 1; i++) {
                    if (!node[path[i]]) {
                        node[path[i]] = {};
                    }
                    node = node[path[i]];
                }
                node[path[i]] = value;
            }
            set(data, [globalize.cldr.attributes.bundle].concat(path), message);
            Globalize.loadMessages(data);
        };

        if (globalize.cldr) {
            if (!getMessage(globalize, path)) {
                defaultMessage = defaultMessage || getDefaultMessage(children);
                setMessage(globalize, path, defaultMessage);
            }
        }
    }
}

function replaceElements(props, formatted) {
    var elements = props.elements;

    function _replaceElements(string, elements) {
        if (typeof string !== "string") {
            throw new Error("Missing or invalid string `" + string + "` (" + typeof string + ")");
        }
        if (typeof elements !== "object") {
            throw new Error("Missing or invalid elements `" + elements + "` (" + typeof elements + ")");
        }

        // Given [x, y, z], it returns [x, element, y, element, z].
        function spreadElementsInBetweenItems(array, element) {
            var getElement = typeof element === "function" ? element : function() {
                return element;
            };
            return array.slice(1).reduce(function(ret, item, i) {
                ret.push(getElement(i), item);
                return ret;
            }, [array[0]]);
        }

        function splice(sourceArray, start, deleteCount, itemsArray) {
            [].splice.apply(sourceArray, [start, deleteCount].concat(itemsArray));
        }

        return Object.keys(elements).reduce(function(ret, key) {
            var element = elements[key];

            ret.forEach(function(string, i) {
                var aux, contents, regexp, regexp2;

                // Insert array into the correct ret position.
                function replaceRetItem(array) {
                    splice(ret, i, 1, array);
                }

                if (typeof string !== "string") {
                    return; // continue;
                }

                // Empty tags, e.g., `[foo/]`.
                aux = string.split("[" + key + "/]");
                if (aux.length > 1) {
                    aux = spreadElementsInBetweenItems(aux, element);
                    replaceRetItem(aux);
                    return; // continue;
                }

                // Start-end tags, e.g., `[foo]content[/foo]`.
                regexp = new RegExp("\\[" + key + "\\][\\s\\S]*?\\[\\/" + key + "\\]", "g");
                regexp2 = new RegExp("\\[" + key + "\\]([\\s\\S]*?)\\[\\/" + key + "\\]");
                aux = string.split(regexp);
                if (aux.length > 1) {
                    contents = string.match(regexp).map(function(content) {
                        return content.replace(regexp2, "$1");
                    });
                    aux = spreadElementsInBetweenItems(aux, function(i) {
                        return React.cloneElement(element, {}, contents[i]);
                    });
                    replaceRetItem(aux);
                }
            });

            return ret;
        }, [string]);
    }

    // Elements replacement.
    if (elements) {
        formatted = React.DOM.span.apply(React.DOM.span, [{}].concat(_replaceElements(formatted, elements)));
    }

    return formatted;
}

function sanitizePath(pathString) {
    return pathString.trim().replace(/\{/g, "(").replace(/\}/g, ")").replace(/\//g, "|").replace(/\n/g, " ").replace(/ +/g, " ").replace(/"/g, "'");
}

// Overload Globalize's `.formatMessage` to allow default message.
var globalizeMessageFormatter = Globalize.messageFormatter;
Globalize.messageFormatter = Globalize.prototype.messageFormatter = function(pathOrMessage) {
    var aux = {};
    var sanitizedPath = sanitizePath(pathOrMessage);

    // Globalize runtime
    if (!this.cldr) {
        // On runtime, the only way for deciding between using sanitizedPath or
        // pathOrMessage as path is by checking which formatter exists.
        arguments[0] = sanitizedPath;
        aux = globalizeMessageFormatter.apply(this, arguments);
        arguments[0] = pathOrMessage;
        return aux || globalizeMessageFormatter.apply(this, arguments);
    }

    var sanitizedPathExists = this.cldr.get(["globalize-messages/{bundle}", sanitizedPath]) !== undefined;
    var pathExists = this.cldr.get(["globalize-messages/{bundle}", pathOrMessage]) !== undefined;

    // Want to distinguish between default message and path value - just checking
    // for sanitizedPath won't be enough, because sanitizedPath !== pathOrMessage
    // for paths like "salutations/hi".
    if (!sanitizedPathExists && !pathExists) {
        aux[this.cldr.attributes.bundle] = {};
        aux[this.cldr.attributes.bundle][sanitizedPath] = pathOrMessage;
        Globalize.loadMessages(aux);
        sanitizedPathExists = true;
    }

    arguments[0] = sanitizedPathExists ? sanitizedPath : pathOrMessage;
    return globalizeMessageFormatter.apply(this, arguments);
};

export default generator("formatMessage", ["path", "variables"], {
    beforeFormat: function(props) {
        messageSetup(this.globalize, props, this.globalizePropValues);
    },
    afterFormat: function(formattedValue) {
        return replaceElements(this.props, formattedValue);
    }
});
<|endoftext|>


<commit_before>

describe("PIXI Stage Component", function() {
  var stagecomponent = createTestFixture({width:300, height:300});
  var mountpoint = null;

  beforeEach(function() { mountpoint = createTestFixtureMountPoint(); });
  afterEach(function() { removeTestFixtureMountPoint(mountpoint); });

  it("creates a canvas used by PIXI", function() {
    React.render(stagecomponent,mountpoint);

    expect(mountpoint.childNodes.length).toBe(1);
    expect(mountpoint.childNodes[0].nodeName).toBe('CANVAS');
    expect(mountpoint.childNodes[0].childNodes.length).toBe(0);
  });

  it("creates a PIXI Stage object", function() {
    var reactinstance = React.render(stagecomponent,mountpoint);

    // hm, probably need some equivalent of getDOMNode
    expect(reactinstance.refs['stage']._displayObject).toBeDefined();

    var stageobject = reactinstance.refs['stage']._displayObject;
    expect(stageobject.stage).toBeDefined();

    // stages are their own stage
    expect(stageobject.stage).toBe(stageobject);
  });

  it("destroys the canvas when the stage is unmounted", function() {
    reactinstance = React.render(stagecomponent,mountpoint);

    // this should unmount the stage and remove the canvas
    var reactinstance = React.render(React.DOM.div(), mountpoint);

    expect(mountpoint.childNodes.length).toBe(1);
    expect(mountpoint.childNodes[0].nodeName).not.toBe('CANVAS');
    expect(mountpoint.childNodes[0].childNodes.length).toBe(0);

    React.unmountComponentAtNode(mountpoint);

    expect(mountpoint.childNodes.length).toBe(0);
  });
});
<commit_msg>Tweaked the test to check to see if this is a ReactPIXI stage<commit_after>

describe("PIXI Stage Component", function() {
  var stagecomponent = createTestFixture({width:300, height:300});
  var mountpoint = null;

  beforeEach(function() { mountpoint = createTestFixtureMountPoint(); });
  afterEach(function() { removeTestFixtureMountPoint(mountpoint); });

  it("creates a canvas used by PIXI", function() {
    React.render(stagecomponent,mountpoint);

    expect(mountpoint.childNodes.length).toBe(1);
    expect(mountpoint.childNodes[0].nodeName).toBe('CANVAS');
    expect(mountpoint.childNodes[0].childNodes.length).toBe(0);
  });

  it("creates a PIXI Stage object", function() {
    var reactinstance = React.render(stagecomponent,mountpoint);

    // hm, probably need some equivalent of getDOMNode
    expect(reactinstance.refs.stage._displayObject).toBeDefined();
    //This should be a ReactPIXI stage.
    expect(reactinstance.refs.stage.renderStage).toBeDefined();
  });

  it("destroys the canvas when the stage is unmounted", function() {
    reactinstance = React.render(stagecomponent,mountpoint);

    // this should unmount the stage and remove the canvas
    var reactinstance = React.render(React.DOM.div(), mountpoint);

    expect(mountpoint.childNodes.length).toBe(1);
    expect(mountpoint.childNodes[0].nodeName).not.toBe('CANVAS');
    expect(mountpoint.childNodes[0].childNodes.length).toBe(0);

    React.unmountComponentAtNode(mountpoint);

    expect(mountpoint.childNodes.length).toBe(0);
  });
});
<|endoftext|>


<commit_before>jQuery(document).ready(function() {
    // form.onsubmit that disables submission - thus allowing handling
    // of search query in JavaScript
    //
    var disableSubmit = function(formId) {
        var form = document.getElementById('search_form');
        form.onsubmit = function(evt) { return false; };
    }

    // load all datasets into index.html from JSON
    //
    var initializeDatasets = function () {
        var http = jQuery.ajax('data/datasets.json');
        http.success(function(data) {
            var datasets = data.datasets;
            var categories = data.categories;
            var indexSource = data.index_source;

            var source    = jQuery('#item-template').html();
            var template  = Handlebars.compile(source);
            var container = jQuery('.featured-datasets .wrapper');

            var loadCategories = function(categories) {
                var subsets, dataToRender, rendered;

                for (var key in categories) {
                    subsets = categories[key].map(function(title_id) {
                        return datasets[title_id];
                    });
                    dataToRender = {category:key, datasets:subsets};
                    rendered = template(dataToRender);

                    container.append(rendered);
                };
            }

            // set up in-memory search
            disableSubmit('search_form');
            loadCategories(categories);
            initializeSearch(data.index_source, container, template);

            // "Reset" button reloads categories
            jQuery('#reset-input').click(function() {
                container.text('');
                loadCategories(categories);
            });
        });
    }

    var initializeSearch = function(list, container, template) {
        var options = {
            caseSensitive: false,
            includeScore: true,
            shouldSort: true,
            keys: ['title', 'summary', 'description']
        };
        var fuse = new Fuse(list, options);
        var input = jQuery('#search-input');

        var onInputChange = function(query) {
            var searchResults = fuse.search(query);
            var sorted = searchResults
                .sort(function(a, b) { return b.score - a.score; })
                .map(function(result) {
                    // round search relevance to 2 decimal places
                    result.item.relevance =
                        +(Math.round(result.score + "e+2")  + "e-2");

                    return result.item;
                });
            var record = {
                category: ('Number of search results: ' + searchResults.length),
                datasets: sorted
            }
            var renderedHtml = template(record);

            container.text('');
            container.append(renderedHtml);

            // console.log(
            //     'COUNT:', sorted.length
            // );
        };

        var lastVal = '';
        input.on('keyup', function(evt) {
            var currVal = input.val()
                .replace(/^\s+/, '')
                .replace(/\s+$/, '');

            if (lastVal !== currVal) {
                onInputChange(currVal);
                lastVal = currVal;
            }

            return true;
        });
    };

    initializeDatasets();
})
<commit_msg>no need to reverse sort by search relevance<commit_after>jQuery(document).ready(function() {
    // form.onsubmit that disables submission - thus allowing handling
    // of search query in JavaScript
    //
    var disableSubmit = function(formId) {
        var form = document.getElementById('search_form');
        form.onsubmit = function(evt) { return false; };
    }

    // load all datasets into index.html from JSON
    //
    var initializeDatasets = function () {
        var http = jQuery.ajax('data/datasets.json');
        http.success(function(data) {
            var datasets = data.datasets;
            var categories = data.categories;
            var indexSource = data.index_source;

            var source    = jQuery('#item-template').html();
            var template  = Handlebars.compile(source);
            var container = jQuery('.featured-datasets .wrapper');

            var loadCategories = function(categories) {
                var subsets, dataToRender, rendered;

                for (var key in categories) {
                    subsets = categories[key].map(function(title_id) {
                        return datasets[title_id];
                    });
                    dataToRender = {category:key, datasets:subsets};
                    rendered = template(dataToRender);

                    container.append(rendered);
                };
            }

            // set up in-memory search
            disableSubmit('search_form');
            loadCategories(categories);
            initializeSearch(data.index_source, container, template);

            // "Reset" button reloads categories
            jQuery('#reset-input').click(function() {
                container.text('');
                loadCategories(categories);
            });
        });
    }

    var initializeSearch = function(list, container, template) {
        var options = {
            caseSensitive: false,
            includeScore: true,
            shouldSort: true,
            keys: ['title', 'summary', 'description']
        };
        var fuse = new Fuse(list, options);
        var input = jQuery('#search-input');

        var onInputChange = function(query) {
            var searchResults = fuse.search(query);
            var sorted = searchResults
                .map(function(result) {
                    // round search relevance to 2 decimal places
                    result.item.relevance =
                        +(Math.round(result.score + "e+2")  + "e-2");

                    return result.item;
                });
            var record = {
                category: ('Number of search results: ' + searchResults.length),
                datasets: sorted
            }
            var renderedHtml = template(record);

            container.text('');
            container.append(renderedHtml);

            // console.log(
            //     'COUNT:', sorted.length
            // );
        };

        var lastVal = '';
        input.on('keyup', function(evt) {
            var currVal = input.val()
                .replace(/^\s+/, '')
                .replace(/\s+$/, '');

            if (lastVal !== currVal) {
                onInputChange(currVal);
                lastVal = currVal;
            }

            return true;
        });
    };

    initializeDatasets();
})
<|endoftext|>


<commit_before>
  basis.require('basis.data');
  basis.require('basis.data.dataset');

 /**
  * @namespace basis.data.dataset
  */

  var namespace = this.path;

  //
  // import names
  //

  var Class = basis.Class;
  var DataObject = basis.data.Object;
  var Slot = basis.data.Slot;
  var SourceDataset = basis.data.dataset.SourceDataset;

  var $undef = basis.fn.$undef;
  var $true = basis.fn.$true;
  var defaultRule = basis.getter($undef);

  //
  // main part
  //

 /**
  * Returns delta object
  * @param {Array.<basis.data.DataObject>} inserted
  * @param {Array.<basis.data.DataObject>} deleted
  * @return {object|boolean}
  */
  function getDelta(inserted, deleted){
    var delta = {};
    var result;

    if (inserted && inserted.length)
      result = delta.inserted = inserted;

    if (deleted && deleted.length)
      result = delta.deleted = deleted;

    if (result)
      return delta;
  }

  var VectorFn = basis.Class(null, { extendConstructor_: true });

  var Count = VectorFn.subclass({
    className: namespace + '.Count',
    add: function(curValue, value){
      return curValue + !!value;
    },
    remove: function(curValue, value){
      return curValue - !!value;
    },
    update: function(curValue, oldValue, newValue){
      return curValue - !!oldValue + !!newValue;
    }
  });

  var Sum = VectorFn.subclass({
    className: namespace + '.Sum',
    add: function(curValue, value){
      return curValue + value;
    },
    remove: function(curValue, value){
      return curValue - value;
    },
    update: function(curValue, oldValue, newValue){
      return curValue - oldValue + newValue;
    }
  });

  var fnPreset_ = {};
  function FnConstructor(BaseClass, getter, events){
    if (!Class.isClass(BaseClass) || !BaseClass.isSubclassOf(VectorFn))
      throw 'Wrong class for index constructor';

    getter = basis.getter(getter);
    events = events || 'update';

    if (typeof events != 'string')
      throw 'Events must be a event names space separated string';

    events = events.qw().sort();

    var fnId = [BaseClass.basisClassId_, getter.basisGetterId_, events].join('_');
    var fnConstructor = fnPreset_[fnId];

    if (fnConstructor)
      return fnConstructor;

    //
    // Create new constructor
    //

    var eventMap = {};
    for (var i = 0; i < events.length; i++)
      eventMap[events[i]] = true;

    return fnPreset_[fnId] = new BaseClass({
      updateEvents: eventMap,
      valueGetter: getter
    });
  }

  function fnConstructor(FnClass){
    return function(getter, events){
      return new FnConstructor(FnClass, getter || $true, events);
    };
  }

  var sum = fnConstructor(Sum);
  var count = fnConstructor(Count);

  //////////
  //
  //

  function recalcSourceObject(dataset, sourceObjectInfo){
    var calcs = dataset.calcs;
    var updateData = {};
    var member = dataset.memberMap_[sourceObjectInfo.key];
    var item = member.item;

    for (var calcName in calcs)
    {
      var calc = calcs[calcName];
      var value = calc.valueGetter(sourceObjectInfo.object);
      updateData[calcName] = calc.update(item.data[calcName] || 0, sourceObjectInfo.values[calcName], value, member);
      sourceObjectInfo.values[calcName] = value;
    }

    item.update(updateData); // TODO: store items and update aside
  }

  function changeSourceObjectKey(dataset, newKey, sourceObjectInfo, recalc){
    var objectId = sourceObjectInfo.object.basisObjectId;
    var oldMember = dataset.memberMap_[sourceObjectInfo.key];
    var newMember = dataset.memberMap_[newKey];
    var calcs = dataset.calcs;
    var inserted;
    var deleted;

    //
    // remove from old member
    //
    var oldItem = oldMember.item;

    // delete references
    delete oldMember[objectId];

    // process member
    if (--oldMember.count == 0)
    {
      // delete
      delete dataset.memberMap_[sourceObjectInfo.key];
      deleted = oldItem;
    }
    else
    {
      // update item
      var updateData = {};

      for (var calcName in calcs)                  // cur value            what remove                 count of values
        updateData[calcName] = calcs[calcName].remove(oldItem.data[calcName], sourceObjectInfo.values[calcName], oldMember);

      oldItem.update(updateData); // TODO: store items and update aside
    }

    //
    // recalc
    //
    if (recalc)
      for (var calcName in calcs)
        sourceObjectInfo.values[calcName] = calcs[calcName].valueGetter(sourceObjectInfo.object);

    //
    // add create or update new member
    //

    var newItem;
    // create member if necessary
    if (!newMember)
    {
      newItem = new Item({
        key: newKey
      });
      newMember = {
        count: 0,
        item: newItem
      };
      dataset.memberMap_[newKey] = newMember;
      inserted = newItem;

      if (dataset.slots_[newKey])
        dataset.slots_[newKey].setDelegate(newItem);
    }
    else
      newItem = newMember.item;

    newMember[objectId] = sourceObjectInfo;
    newMember.count++;

    var updateData = {};

    for (var calcName in calcs)               // cur value                    what remove         count of values
      updateData[calcName] = calcs[calcName].add(newItem.data[calcName] || 0, sourceObjectInfo.values[calcName], newMember);

    newItem.update(updateData); // TODO: store items and update aside

    // update key

    newItem.key = newKey;
    sourceObjectInfo.key = newKey;

    //
    return {
      inserted: inserted,
      deleted: deleted
    };
  }

  var VECTOR_ITEM_HANDLER = {
    update: function(object){
      var sourceObjectInfo = this.sourceMap_[object.basisObjectId];
      var key = this.rule(object);

      if (sourceObjectInfo.key !== key)
      {
        var delta = changeSourceObjectKey(this, key, sourceObjectInfo, true);
        if (delta = getDelta(delta.inserted && [delta.inserted], delta.deleted && [delta.deleted]))
          this.event_datasetChanged(delta);
      }
      else
        recalcSourceObject(this, sourceObjectInfo);
    }
  };

  var VECTOR_SOURCE_HANDLER = {
    datasetChanged: function(sender, delta){
      var sourceInserted = delta.inserted;
      var sourceDeleted = delta.deleted;
      var memberMap = this.memberMap_;
      var sourceMap = this.sourceMap_;
      var calcs = this.calcs;
      var inserted = [];
      var deleted = [];

      if (sourceInserted)
        for (var i = 0, object; object = sourceInserted[i]; i++)
        {
          var objectId = object.basisObjectId;
          var key = this.rule(object);
          var member = this.memberMap_[key];
          var item;

          // create member if necessary
          if (!member)
          {
            item = new Item({
              key: key
            });
            member = {
              count: 0,
              item: item
            };
            this.memberMap_[key] = member;
            inserted.push(item);

            if (this.slots_[key])
              this.slots_[key].setDelegate(item);
          }
          else
            item = member.item;

          // create source object and registrate it
          var sourceObjectInfo = {
            key: key,
            object: object,
            values: {}
          };

          sourceMap[objectId] = sourceObjectInfo;

          member[objectId] = sourceObjectInfo;
          member.count++;

          // add handler
          object.addHandler(VECTOR_ITEM_HANDLER, this);

          var updateData = {};
          for (var calcName in calcs)
          {
            var calc = calcs[calcName];
            var value = calc.valueGetter(object);
            sourceObjectInfo.values[calcName] = value;
                                    // cur value              what remove         count of values
            updateData[calcName] = calc.add(item.data[calcName] || 0, value, member);
          }

          item.update(updateData); // TODO: store items and update aside
        }

      if (sourceDeleted)
        for (var i = 0, object; object = sourceDeleted[i]; i++)
        {
          var objectId = object.basisObjectId;
          var sourceObjectInfo = sourceMap[objectId];
          var member = memberMap[sourceObjectInfo.key];
          var item = member.item;

          // delete references
          delete sourceMap[objectId];
          delete member[objectId];

          // remove handler
          object.removeHandler(VECTOR_ITEM_HANDLER, this);

          // process member
          if (--member.count == 0)
          {
            // delete
            delete memberMap[sourceObjectInfo.key];
            deleted.push(item);
          }
          else
          {
            // update item
            var updateData = {};

            for (var calcName in calcs)          // cur value            what remove                 count of values
              updateData[calcName] = calcs[calcName].remove(item.data[calcName], sourceObjectInfo.values[calcName], member);

            item.update(updateData); // TODO: store items and update aside
          }
        }

      // fire event
      if (delta = getDelta(inserted, deleted))
        this.event_datasetChanged(delta);
    }
  };

  var Item = Class(DataObject, {
    className: namespace + '.Item',
    isTarget: true,
    key: undefined
  });

  var Vector = Class(SourceDataset, {
    className: namespace + '.Vector',
   /**
    * @type {Object}
    */
    calcs: null,
    slots_: null,

    rule: defaultRule,

    event_datasetChanged: function(delta){
      SourceDataset.prototype.event_datasetChanged.call(this, delta);

      // destroy deleted
      if (delta.deleted)
        delta.deleted.forEach(function(item){
          item.destroy();
        });
    },

    listen: {
      source: VECTOR_SOURCE_HANDLER
    },

   /**
    * @inheritDoc
    */
    init: function(){
      // process calcs
      var calcs = this.calcs;
      this.calcs = {};
      for (var key in calcs)
      {
        var calc = calcs[key];
        if (calc instanceof VectorFn == false)
          calc = sum(calc);
        this.calcs[key] = calc;
      }

      this.slots_ = {};

      // inherit
      SourceDataset.prototype.init.call(this);
    },

    get: function(key){
      var member = this.memberMap_[key];
      if (member)
        return member.item;
    },
    getSlot: function(key){
      var slot = this.slots_[key];
      if (!slot)
      {
        slot = new Slot({
          key: key,
          delegate: this.get(key)
        });
        this.slots_[key] = slot;
      }
      return slot;
    },

    setRule: function(rule){
      if (typeof rule != 'function')
        rule = defaultRule;

      if (this.rule !== rule)
      {
        this.rule = rule;
        this.applyRule();
      }
    },

    setCalc: function(name, newCalc){
      if (newCalc && newCalc instanceof VectorFn == false)
        newCalc = sum(newCalc);

	    var oldCalc = this.calcs[name];
      if (oldCalc != newCalc)
      {
        var sourceMap = this.sourceMap_;

  	    if (newCalc)
  	    {
          this.calcs[name] = newCalc;

          var newValues = {};
          for (var objectId in sourceMap)
          {
            var sourceObjectInfo = sourceMap[objectId];
            var key = sourceObjectInfo.key;
            var member = this.memberMap_[key];

            if (!newValues[key])
            {
              newValues[key] = {
                item: member.item,
                value: 0
              }
            }

            sourceObjectInfo.values[name] = newCalc.valueGetter(sourceObjectInfo.object);
            newValues[key].value = newCalc.add(newValues[key].value || 0, sourceObjectInfo.values[name], member);
          }

          var updateData = {};
          for (var key in newValues)
          {
            updateData[name] = newValues[key].value;
            newValues[key].item.update(updateData);
          }
        }
        else
        {
          delete this.calcs[name];

          for (var objectId in sourceMap)
            delete sourceMap[objectId].values[name];

          var resetData = {};
          resetData[name] = undefined;
          for (var i = 0, item, items = this.getItems(); item = items[i]; i++)
            item.update(resetData);
        }
      }
    },

    applyRule: function(scope){
      var inserted = [];
      var deleted = [];
      var delta;

     if (!scope)
       scope = this.sourceMap_;

      for (var objectId in scope)
      {
        var sourceObjectInfo = scope[objectId];
        var newKey = this.rule(sourceObjectInfo.object);
        if (newKey !== sourceObjectInfo.key)
        {
          delta = changeSourceObjectKey(this, newKey, sourceObjectInfo, true);
          if (delta.inserted)
            inserted.push(delta.inserted);
          if (delta.deleted)
            deleted.push(delta.deleted);
        }
        else
          recalcSourceObject(this, sourceObjectInfo);
      }

      if (delta = getDelta(inserted, deleted))
        this.event_datasetChanged(delta);
    }
  });

  module.exports = {
    Vector: Vector,

    count: count,
    sum: sum
  };
<commit_msg>recalc values if member has slot<commit_after>
  basis.require('basis.data');
  basis.require('basis.data.dataset');

 /**
  * @namespace basis.data.dataset
  */

  var namespace = this.path;

  //
  // import names
  //

  var Class = basis.Class;
  var DataObject = basis.data.Object;
  var Slot = basis.data.Slot;
  var SourceDataset = basis.data.dataset.SourceDataset;

  var $undef = basis.fn.$undef;
  var $true = basis.fn.$true;
  var defaultRule = basis.getter($undef);

  //
  // main part
  //

 /**
  * Returns delta object
  * @param {Array.<basis.data.DataObject>} inserted
  * @param {Array.<basis.data.DataObject>} deleted
  * @return {object|boolean}
  */
  function getDelta(inserted, deleted){
    var delta = {};
    var result;

    if (inserted && inserted.length)
      result = delta.inserted = inserted;

    if (deleted && deleted.length)
      result = delta.deleted = deleted;

    if (result)
      return delta;
  }

  var VectorFn = basis.Class(null, { extendConstructor_: true });

  var Count = VectorFn.subclass({
    className: namespace + '.Count',
    add: function(curValue, value){
      return curValue + !!value;
    },
    remove: function(curValue, value){
      return curValue - !!value;
    },
    update: function(curValue, oldValue, newValue){
      return curValue - !!oldValue + !!newValue;
    }
  });

  var Sum = VectorFn.subclass({
    className: namespace + '.Sum',
    add: function(curValue, value){
      return curValue + value;
    },
    remove: function(curValue, value){
      return curValue - value;
    },
    update: function(curValue, oldValue, newValue){
      return curValue - oldValue + newValue;
    }
  });

  var fnPreset_ = {};
  function FnConstructor(BaseClass, getter, events){
    if (!Class.isClass(BaseClass) || !BaseClass.isSubclassOf(VectorFn))
      throw 'Wrong class for index constructor';

    getter = basis.getter(getter);
    events = events || 'update';

    if (typeof events != 'string')
      throw 'Events must be a event names space separated string';

    events = events.qw().sort();

    var fnId = [BaseClass.basisClassId_, getter.basisGetterId_, events].join('_');
    var fnConstructor = fnPreset_[fnId];

    if (fnConstructor)
      return fnConstructor;

    //
    // Create new constructor
    //

    var eventMap = {};
    for (var i = 0; i < events.length; i++)
      eventMap[events[i]] = true;

    return fnPreset_[fnId] = new BaseClass({
      updateEvents: eventMap,
      valueGetter: getter
    });
  }

  function fnConstructor(FnClass){
    return function(getter, events){
      return new FnConstructor(FnClass, getter || $true, events);
    };
  }

  var sum = fnConstructor(Sum);
  var count = fnConstructor(Count);

  //////////
  //
  //

  function recalcSourceObject(dataset, sourceObjectInfo){
    var calcs = dataset.calcs;
    var updateData = {};
    var member = dataset.memberMap_[sourceObjectInfo.key];
    var item = member.item;

    for (var calcName in calcs)
    {
      var calc = calcs[calcName];
      var value = calc.valueGetter(sourceObjectInfo.object);
      updateData[calcName] = calc.update(item.data[calcName] || 0, sourceObjectInfo.values[calcName], value, member);
      sourceObjectInfo.values[calcName] = value;
    }

    item.update(updateData); // TODO: store items and update aside
  }

  function changeSourceObjectKey(dataset, newKey, sourceObjectInfo, recalc){
    var objectId = sourceObjectInfo.object.basisObjectId;
    var oldMember = dataset.memberMap_[sourceObjectInfo.key];
    var newMember = dataset.memberMap_[newKey];
    var calcs = dataset.calcs;
    var inserted;
    var deleted;

    //
    // remove from old member
    //
    var oldItem = oldMember.item;

    // delete references
    delete oldMember[objectId];

    // process member
    if (--oldMember.count == 0)
    {
      // delete
      delete dataset.memberMap_[sourceObjectInfo.key];
      deleted = oldItem;
    }
    else
    {
      // update item
      var updateData = {};

      for (var calcName in calcs)                  // cur value            what remove                 count of values
        updateData[calcName] = calcs[calcName].remove(oldItem.data[calcName], sourceObjectInfo.values[calcName], oldMember);

      oldItem.update(updateData); // TODO: store items and update aside
    }

    //
    // recalc
    //
    if (recalc)
      for (var calcName in calcs)
        sourceObjectInfo.values[calcName] = calcs[calcName].valueGetter(sourceObjectInfo.object);

    //
    // add create or update new member
    //

    var newItem;
    // create member if necessary
    if (!newMember)
    {
      newItem = new Item({
        key: newKey
      });
      newMember = {
        count: 0,
        item: newItem
      };
      dataset.memberMap_[newKey] = newMember;
      inserted = newItem;

      if (dataset.slots_[newKey])
        dataset.slots_[newKey].setDelegate(newItem);
    }
    else
      newItem = newMember.item;

    newMember[objectId] = sourceObjectInfo;
    newMember.count++;

    var updateData = {};

    for (var calcName in calcs)               // cur value                    what remove         count of values
      updateData[calcName] = calcs[calcName].add(newItem.data[calcName] || 0, sourceObjectInfo.values[calcName], newMember);

    newItem.update(updateData); // TODO: store items and update aside

    // update key

    newItem.key = newKey;
    sourceObjectInfo.key = newKey;

    //
    return {
      inserted: inserted,
      deleted: deleted
    };
  }

  var VECTOR_ITEM_HANDLER = {
    update: function(object){
      var sourceObjectInfo = this.sourceMap_[object.basisObjectId];
      var key = this.rule(object);

      if (sourceObjectInfo.key !== key)
      {
        var delta = changeSourceObjectKey(this, key, sourceObjectInfo, true);
        if (delta = getDelta(delta.inserted && [delta.inserted], delta.deleted && [delta.deleted]))
          this.event_datasetChanged(delta);
      }
      else
        recalcSourceObject(this, sourceObjectInfo);
    }
  };

  var VECTOR_SOURCE_HANDLER = {
    datasetChanged: function(sender, delta){
      var sourceInserted = delta.inserted;
      var sourceDeleted = delta.deleted;
      var memberMap = this.memberMap_;
      var sourceMap = this.sourceMap_;
      var calcs = this.calcs;
      var inserted = [];
      var deleted = [];

      if (sourceInserted)
        for (var i = 0, object; object = sourceInserted[i]; i++)
        {
          var objectId = object.basisObjectId;
          var key = this.rule(object);
          var member = this.memberMap_[key];
          var item;

          // create member if necessary
          if (!member)
          {
            item = new Item({
              key: key
            });
            member = {
              count: 0,
              item: item
            };
            this.memberMap_[key] = member;
            inserted.push(item);

            if (this.slots_[key])
              this.slots_[key].setDelegate(item);
          }
          else
            item = member.item;

          // create source object and registrate it
          var sourceObjectInfo = {
            key: key,
            object: object,
            values: {}
          };

          sourceMap[objectId] = sourceObjectInfo;

          member[objectId] = sourceObjectInfo;
          member.count++;

          // add handler
          object.addHandler(VECTOR_ITEM_HANDLER, this);

          var updateData = {};
          for (var calcName in calcs)
          {
            var calc = calcs[calcName];
            var value = calc.valueGetter(object);
            sourceObjectInfo.values[calcName] = value;
                                    // cur value              what remove         count of values
            updateData[calcName] = calc.add(item.data[calcName] || 0, value, member);
          }

          item.update(updateData); // TODO: store items and update aside
        }

      if (sourceDeleted)
        for (var i = 0, object; object = sourceDeleted[i]; i++)
        {
          var objectId = object.basisObjectId;
          var sourceObjectInfo = sourceMap[objectId];
          var member = memberMap[sourceObjectInfo.key];
          var item = member.item;

          // delete references
          delete sourceMap[objectId];
          delete member[objectId];

          // remove handler
          object.removeHandler(VECTOR_ITEM_HANDLER, this);

          // process member
          if (--member.count == 0)
          {
            // delete
            delete memberMap[sourceObjectInfo.key];
            deleted.push(item);

            if (this.slots_[item.key])
            {
              // update item
              var updateData = {};

              for (var calcName in calcs)          // cur value            what remove                 count of values
                updateData[calcName] = calcs[calcName].remove(item.data[calcName], sourceObjectInfo.values[calcName], member);

              item.update(updateData); // TODO: store items and update aside
            }
          }
          else
          {
            // update item
            var updateData = {};

            for (var calcName in calcs)          // cur value            what remove                 count of values
              updateData[calcName] = calcs[calcName].remove(item.data[calcName], sourceObjectInfo.values[calcName], member);

            item.update(updateData); // TODO: store items and update aside
          }
        }

      // fire event
      if (delta = getDelta(inserted, deleted))
        this.event_datasetChanged(delta);
    }
  };

  var Item = Class(DataObject, {
    className: namespace + '.Item',
    isTarget: true,
    key: undefined
  });

  var Vector = Class(SourceDataset, {
    className: namespace + '.Vector',
   /**
    * @type {Object}
    */
    calcs: null,
    slots_: null,

    rule: defaultRule,

    event_datasetChanged: function(delta){
      SourceDataset.prototype.event_datasetChanged.call(this, delta);

      // destroy deleted
      if (delta.deleted)
        delta.deleted.forEach(function(item){
          item.destroy();
        });
    },

    listen: {
      source: VECTOR_SOURCE_HANDLER
    },

   /**
    * @inheritDoc
    */
    init: function(){
      // process calcs
      var calcs = this.calcs;
      this.calcs = {};
      for (var key in calcs)
      {
        var calc = calcs[key];
        if (calc instanceof VectorFn == false)
          calc = sum(calc);
        this.calcs[key] = calc;
      }

      this.slots_ = {};

      // inherit
      SourceDataset.prototype.init.call(this);
    },

    get: function(key){
      var member = this.memberMap_[key];
      if (member)
        return member.item;
    },
    getSlot: function(key){
      var slot = this.slots_[key];
      if (!slot)
      {
        slot = new Slot({
          key: key,
          delegate: this.get(key)
        });
        this.slots_[key] = slot;
      }
      return slot;
    },

    setRule: function(rule){
      if (typeof rule != 'function')
        rule = defaultRule;

      if (this.rule !== rule)
      {
        this.rule = rule;
        this.applyRule();
      }
    },

    setCalc: function(name, newCalc){
      if (newCalc && newCalc instanceof VectorFn == false)
        newCalc = sum(newCalc);

	    var oldCalc = this.calcs[name];
      if (oldCalc != newCalc)
      {
        var sourceMap = this.sourceMap_;

  	    if (newCalc)
  	    {
          this.calcs[name] = newCalc;

          var newValues = {};
          for (var objectId in sourceMap)
          {
            var sourceObjectInfo = sourceMap[objectId];
            var key = sourceObjectInfo.key;
            var member = this.memberMap_[key];

            if (!newValues[key])
            {
              newValues[key] = {
                item: member.item,
                value: 0
              }
            }

            sourceObjectInfo.values[name] = newCalc.valueGetter(sourceObjectInfo.object);
            newValues[key].value = newCalc.add(newValues[key].value || 0, sourceObjectInfo.values[name], member);
          }

          var updateData = {};
          for (var key in newValues)
          {
            updateData[name] = newValues[key].value;
            newValues[key].item.update(updateData);
          }
        }
        else
        {
          delete this.calcs[name];

          for (var objectId in sourceMap)
            delete sourceMap[objectId].values[name];

          var resetData = {};
          resetData[name] = undefined;
          for (var i = 0, item, items = this.getItems(); item = items[i]; i++)
            item.update(resetData);
        }
      }
    },

    applyRule: function(scope){
      var inserted = [];
      var deleted = [];
      var delta;

     if (!scope)
       scope = this.sourceMap_;

      for (var objectId in scope)
      {
        var sourceObjectInfo = scope[objectId];
        var newKey = this.rule(sourceObjectInfo.object);
        if (newKey !== sourceObjectInfo.key)
        {
          delta = changeSourceObjectKey(this, newKey, sourceObjectInfo, true);
          if (delta.inserted)
            inserted.push(delta.inserted);
          if (delta.deleted)
            deleted.push(delta.deleted);
        }
        else
          recalcSourceObject(this, sourceObjectInfo);
      }

      if (delta = getDelta(inserted, deleted))
        this.event_datasetChanged(delta);
    }
  });

  module.exports = {
    Vector: Vector,

    count: count,
    sum: sum
  };
<|endoftext|>


<commit_before>// creating arrays
var day1 = [76, 81, 79]

console.log("day 1 temperature readings: ", day1)

// accessing elements of the array
var day1MorningTemp = day1[0]
var day1MiddayTemp = day1[1]
var day1EveningTemp = day1[2]

console.log("Day 1 8am temperature: " + day1MorningTemp)
console.log("Day 1 12pm temperature: " + day1MiddayTemp)
console.log("Day 1 6pm temperature: " + day1EveningTemp)

console.log("\nPrint again...")
console.log("Day 1 8am temperature: " + day1[0])
console.log("Day 1 12pm temperature: " + day1[1])
console.log("Day 1 6pm temperature: " + day1[2])
<commit_msg>code sample update for cs05s11/example-01.js<commit_after>// creating arrays
var day1 = [76, 81, 79]

console.log("day 1 temperature readings: ", day1)

// accessing elements of the array
var day1MorningTemp = day1[0]
var day1MiddayTemp = day1[1]
var day1EveningTemp = day1[2]

console.log("Day 1 8am temperature: " + day1MorningTemp)
console.log("Day 1 12pm temperature: " + day1MiddayTemp)
console.log("Day 1 6pm temperature: " + day1EveningTemp)

console.log("\nPrint again...")
console.log("Day 1 8am temperature: " + day1[0])
console.log("Day 1 12pm temperature: " + day1[1])
console.log("Day 1 6pm temperature: " + day1[2])

/*
 * Output:
 *------------------

day 1 temperature readings:  [ 76, 81, 79 ]
Day 1 8am temperature: 76
Day 1 12pm temperature: 81
Day 1 6pm temperature: 79

Print again...
Day 1 8am temperature: 76
Day 1 12pm temperature: 81
Day 1 6pm temperature: 79

*/
<|endoftext|>


<commit_before><commit_msg>Changed onBlur so it works with current Atom version<commit_after><|endoftext|>


<commit_before>// Copyright 2015 Endless Mobile, Inc.

const Format = imports.format;
const Gettext = imports.gettext;
const GObject = imports.gi.GObject;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;

const Actions = imports.app.actions;
const Config = imports.app.config;
const Dispatcher = imports.app.dispatcher;
const Module = imports.app.interfaces.module;
const StyleClasses = imports.app.styleClasses;
const Utils = imports.app.utils;

String.prototype.format = Format.format;
let _ = Gettext.dgettext.bind(null, Config.GETTEXT_PACKAGE);

/**
 * Class: SearchBannerModule
 * Banner with status information about search results
 *
 * CSS classes:
 *   title - on the banner
 *   results-message - on the banner when displaying status about results
 *   error-message - on the banner when displaying status about an error
 *   query - on the portion of the banner indicating a user query string
 */
const SearchBannerModule = new Lang.Class({
    Name: 'SearchBannerModule',
    GTypeName: 'EknSearchBannerModule',
    Extends: Gtk.Label,
    Implements: [ Module.Module ],

    Properties: {
        'factory': GObject.ParamSpec.override('factory', Module.Module),
        'factory-name': GObject.ParamSpec.override('factory-name', Module.Module),
    },

    Template: 'resource:///com/endlessm/knowledge/data/widgets/searchBannerModule.ui',

    _init: function (props={}) {
        this.parent(props);
        Dispatcher.get_default().register((payload) => {
            let context = this.get_style_context();
            switch(payload.action_type) {
                case Actions.SEARCH_STARTED:
                    context.remove_class(StyleClasses.ERROR_MESSAGE);
                    context.add_class(StyleClasses.RESULTS_MESSAGE_TITLE);
                    /* TRANSLATORS: This message is displayed while an app is
                    searching for results. The %s will be replaced with the term
                    that the user searched for. Note, in English, it is
                    surrounded by Unicode left and right double quotes (U+201C
                    and U+201D). Make sure to include %s in your translation and
                    use whatever quote marks are appropriate for your language. */
                    this.label = Utils.format_ui_string(this.get_style_context(), _("Searching for “%s”"),
                        payload.query, StyleClasses.QUERY);
                    break;
                case Actions.SEARCH_READY:
                    context.remove_class(StyleClasses.ERROR_MESSAGE);
                    context.add_class(StyleClasses.RESULTS_MESSAGE_TITLE);
                    /* TRANSLATORS: This message is displayed when an app is
                    done searching for results. The %s will be replaced with the
                    term that the user searched for. Note, in English, it is
                    surrounded by Unicode left and right double quotes (U+201C
                    and U+201D). Make sure to include %s in your translation and
                    use whatever quote marks are appropriate for your language. */
                    this.label = Utils.format_ui_string(this.get_style_context(), _("Results were found for “%s”"),
                        payload.query, StyleClasses.QUERY);
                    break;
                case Actions.SEARCH_FAILED:
                    context.remove_class(StyleClasses.RESULTS_MESSAGE_TITLE);
                    context.add_class(StyleClasses.ERROR_MESSAGE);
                    this.label = _("OOPS!");
                    break;
            }
        });
    },
});
<commit_msg>searchBannerModule: don't assert results were found<commit_after>// Copyright 2015 Endless Mobile, Inc.

const Format = imports.format;
const Gettext = imports.gettext;
const GObject = imports.gi.GObject;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;

const Actions = imports.app.actions;
const Config = imports.app.config;
const Dispatcher = imports.app.dispatcher;
const Module = imports.app.interfaces.module;
const StyleClasses = imports.app.styleClasses;
const Utils = imports.app.utils;

String.prototype.format = Format.format;
let _ = Gettext.dgettext.bind(null, Config.GETTEXT_PACKAGE);

/**
 * Class: SearchBannerModule
 * Banner with status information about search results
 *
 * CSS classes:
 *   title - on the banner
 *   results-message - on the banner when displaying status about results
 *   error-message - on the banner when displaying status about an error
 *   query - on the portion of the banner indicating a user query string
 */
const SearchBannerModule = new Lang.Class({
    Name: 'SearchBannerModule',
    GTypeName: 'EknSearchBannerModule',
    Extends: Gtk.Label,
    Implements: [ Module.Module ],

    Properties: {
        'factory': GObject.ParamSpec.override('factory', Module.Module),
        'factory-name': GObject.ParamSpec.override('factory-name', Module.Module),
    },

    Template: 'resource:///com/endlessm/knowledge/data/widgets/searchBannerModule.ui',

    _init: function (props={}) {
        this.parent(props);
        Dispatcher.get_default().register((payload) => {
            let context = this.get_style_context();
            switch(payload.action_type) {
                case Actions.SEARCH_STARTED:
                    context.remove_class(StyleClasses.ERROR_MESSAGE);
                    context.add_class(StyleClasses.RESULTS_MESSAGE_TITLE);
                    /* TRANSLATORS: This message is displayed while an app is
                    searching for results. The %s will be replaced with the term
                    that the user searched for. Note, in English, it is
                    surrounded by Unicode left and right double quotes (U+201C
                    and U+201D). Make sure to include %s in your translation and
                    use whatever quote marks are appropriate for your language. */
                    this.label = Utils.format_ui_string(this.get_style_context(), _("Searching for “%s”"),
                        payload.query, StyleClasses.QUERY);
                    break;
                case Actions.SEARCH_READY:
                    context.remove_class(StyleClasses.ERROR_MESSAGE);
                    context.add_class(StyleClasses.RESULTS_MESSAGE_TITLE);
                    /* TRANSLATORS: This message is displayed when an app is
                    done searching for results. The %s will be replaced with the
                    term that the user searched for. Note, in English, it is
                    surrounded by Unicode left and right double quotes (U+201C
                    and U+201D). Make sure to include %s in your translation and
                    use whatever quote marks are appropriate for your language. */
                    this.label = Utils.format_ui_string(this.get_style_context(), _("Results for “%s”"),
                        payload.query, StyleClasses.QUERY);
                    break;
                case Actions.SEARCH_FAILED:
                    context.remove_class(StyleClasses.RESULTS_MESSAGE_TITLE);
                    context.add_class(StyleClasses.ERROR_MESSAGE);
                    this.label = _("OOPS!");
                    break;
            }
        });
    },
});
<|endoftext|>


<commit_before>'use strict'

/*
 MIT License

 Copyright (c) 2016 Ilya Shaisultanov

 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:

 The above copyright notice and this permission notice shall be included in all
 copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */

var dgram = require('dgram')
  , EE = require('events').EventEmitter
  , util = require('util')
  , ip = require('ip')
  , debug = require('debug')
  , os = require('os')
  , async = require('async')
  , extend = require('extend')
  , SsdpHeader = require('./ssdpHeader')

var httpHeader = /HTTP\/\d{1}\.\d{1} \d+ .*/
  , ssdpHeader = /^([^:]+):\s*(.*)$/

/* consts */
var c = require('./const')

var nodeVersion = process.version.substr(1)
  , moduleVersion = require('../package.json').version
  , moduleName = require('../package.json').name

/**
 * Options:
 *
 * @param {Object} opts
 * @param {String} opts.ssdpSig SSDP signature
 * @param {String} opts.ssdpIp SSDP multicast group
 * @param {String} opts.ssdpPort SSDP port
 * @param {Number} opts.ssdpTtl Multicast TTL
 * @param {Number} opts.adInterval Interval at which to send out advertisement (ms)
 * @param {String} opts.description Path to SSDP description file
 * @param {String} opts.udn SSDP Unique Device Name
 * @param {Object} opts.headers Additional headers
 * @param {Array} opts.interfaces Names of interfaces to use. When set, other interfaces are ignored.
 *
 * @param {Number} opts.ttl Packet TTL
 * @param {Boolean} opts.allowWildcards Allow wildcards in M-SEARCH packets (non-standard)
 *
 * @returns {SSDP}
 * @constructor
 */
function SSDP(opts) {
  var self = this

  if (!(this instanceof SSDP)) return new SSDP(opts)

  this._subclass = this._subclass || 'node-ssdp:base'

  opts = opts || {}

  this._logger = opts.customLogger || debug(this._subclass)

  EE.call(self)

  this._init(opts)
}

util.inherits(SSDP, EE)

/**
 * Initializes instance properties.
 * @param opts
 * @private
 */
SSDP.prototype._init = function (opts) {
  this._ssdpSig = opts.ssdpSig || getSsdpSignature()

  this._explicitSocketBind = opts.explicitSocketBind
  this._interfaces = opts.interfaces
  this._reuseAddr = opts.reuseAddr === undefined ? true : opts.reuseAddr

  // User shouldn't need to set these
  this._ssdpIp = opts.ssdpIp || c.SSDP_DEFAULT_IP
  this._ssdpPort = opts.ssdpPort || c.SSDP_DEFAULT_PORT
  this._ssdpTtl = opts.ssdpTtl || 4

  // port on which to listen for messages
  // this generally should be left up to the system for SSDP Client
  // For server, this will be set by the server constructor
  // unless user sets a value.
  this._sourcePort = opts.sourcePort || 0

  this._adInterval = opts.adInterval || 10000

  this._ttl = opts.ttl || 1800

  if (typeof opts.location === 'function') {
    Object.defineProperty(this, '_location', {
      enumerable: true,
      get: opts.location
    })
  } else if (typeof opts.location === 'object') {
    this._locationProtocol = opts.location.protocol || 'http://'
    this._locationPort = opts.location.port
    this._locationPath = opts.location.path
  } else {
    // Probably should specify this explicitly
    this._location = opts.location || 'http://' + ip.address() + ':' + 10293 + '/upnp/desc.html'
  }

  this._ssdpServerHost = this._ssdpIp + ':' + this._ssdpPort

  this._usns = {}
  this._udn = opts.udn || 'uuid:f40c2981-7329-40b7-8b04-27f187aecfb5'

  this._extraHeaders = opts.headers || {}

  this._allowWildcards = opts.allowWildcards

  this._suppressRootDeviceAdvertisements = opts.suppressRootDeviceAdvertisements
}

/**
 * Creates and returns UDP4 socket.
 * Prior to node v0.12.x `dgram.createSocket` did not accept
 * an object and socket reuse was not available; this method
 * contains a crappy workaround to make version of node before 0.12
 * to work correctly. See https://github.com/diversario/node-ssdp/pull/38
 *
 * @returns {Socket}
 * @private
 */
SSDP.prototype._createSockets = function () {
  var interfaces = os.networkInterfaces()
    , self = this

  this.sockets = {}

  Object.keys(interfaces).forEach(function (iName) {
    if (!self._interfaces || self._interfaces.indexOf(iName) > -1) {
      self._logger('discovering all IPs from interface %s', iName)

      interfaces[iName].forEach(function (ipInfo) {
        if (ipInfo.internal == false && ipInfo.family == "IPv4") {
          self._logger('Will use interface %s', iName)
          var socket

          if (parseFloat(process.version.replace(/\w/, '')) >= 0.12) {
            socket = dgram.createSocket({type: 'udp4', reuseAddr: self._reuseAddr})
          } else {
            socket = dgram.createSocket('udp4')
          }

          if (socket) {
            socket.unref()

            self.sockets[ipInfo.address] = socket
          }
        }
      })
    }
  })

  if (Object.keys(this.sockets) == 0) {
    throw new Error('No sockets available, cannot start.')
  }
}

/**
 * Advertise shutdown and close UDP socket.
 */
SSDP.prototype._stop = function () {
  var self = this

  if (!this.sockets) {
    this._logger('Already stopped.')
    return
  }

  Object.keys(this.sockets).forEach(function (ipAddress) {
    var socket = self.sockets[ipAddress]

    socket && socket.close()

    self._logger('Stopped socket on %s', ipAddress)
  })

  this.sockets = null

  this._socketBound = this._started = false
}

/**
 * Configures UDP socket `socket`.
 * Binds event listeners.
 */
SSDP.prototype._start = function (cb) {
  var self = this

  if (self._started) {
    self._logger('Already started.')
    return
  }

  if (!this.sockets) {
    this._createSockets()
  }

  self._started = true

  var interfaces = Object.keys(this.sockets)

  async.each(interfaces, function (iface, next) {
    var socket = self.sockets[iface]

    socket.on('error', function onSocketError(err) {
      self._logger('Socker error: %s', err.message)
    })

    socket.on('message', function onSocketMessage(msg, rinfo) {
      self._parseMessage(msg, rinfo)
    })

    socket.on('listening', function onSocketListening() {
      var addr = socket.address()

      self._logger('SSDP listening: %o', {address: 'http://' + addr.address + ':' + addr.port, 'interface': iface})

      try {
        addMembership()
      } catch (e) {
        if (e.code === 'ENODEV' || e.code === 'EADDRNOTAVAIL') {
          self._logger('Interface %s is not present to add multicast group membership. Scheduling a retry. Error: %s', addr, e.message)
          setTimeout(addMembership, 5000)
        } else {
          throw e
        }
      }

      function addMembership() {
        socket.addMembership(self._ssdpIp, iface) // TODO: specifying the interface in there might make a difference
        socket.setMulticastTTL(self._ssdpTtl)
      }
    })

    if (self._explicitSocketBind) {
      socket.bind(self._sourcePort, iface, next)
    } else {
      socket.bind(self._sourcePort, next) // socket binds on 0.0.0.0
    }
  }, cb)
}

/**
 * Routes a network message to the appropriate handler.
 *
 * @param msg
 * @param rinfo
 */
SSDP.prototype._parseMessage = function (msg, rinfo) {
  msg = msg.toString()

  var type = msg.split('\r\n').shift()

  // HTTP/#.# ### Response to M-SEARCH
  if (httpHeader.test(type)) {
    this._parseResponse(msg, rinfo)
  } else {
    this._parseCommand(msg, rinfo)
  }
}

/**
 * Parses SSDP command.
 *
 * @param msg
 * @param rinfo
 */
SSDP.prototype._parseCommand = function parseCommand(msg, rinfo) {
  var method = this._getMethod(msg)
    , headers = this._getHeaders(msg)

  switch (method) {
    case c.NOTIFY:
      this._notify(headers, msg, rinfo)
      break
    case c.M_SEARCH:
      this._msearch(headers, msg, rinfo)
      break
    default:
      this._logger('Unhandled command: %o', {'message': msg, 'rinfo': rinfo})
  }
}

/**
 * Handles NOTIFY command
 * Emits `advertise-alive`, `advertise-bye` events.
 *
 * @param headers
 * @param msg
 * @param rinfo
 */
SSDP.prototype._notify = function (headers, msg, rinfo) {
  if (!headers.NTS) {
    this._logger('Missing NTS header: %o', headers)
    return
  }

  switch (headers.NTS.toLowerCase()) {
    // Device coming to life.

    case c.SSDP_ALIVE:
      this.emit(c.ADVERTISE_ALIVE, headers, rinfo)
      break

    // Device shutting down.
    case c.SSDP_BYE:
      this.emit(c.ADVERTISE_BYE, headers, rinfo)
      break

    default:
      this._logger('Unhandled NOTIFY event: %o', {'message': msg, 'rinfo': rinfo})
  }
}

/**
 * Handles M-SEARCH command.
 *
 * @param headers
 * @param msg
 * @param rinfo
 */
SSDP.prototype._msearch = function (headers, msg, rinfo) {
  this._logger('SSDP M-SEARCH event: %o', {'ST': headers.ST, 'address': rinfo.address, 'port': rinfo.port})

  if (!headers.MAN || !headers.MX || !headers.ST) return

  this._respondToSearch(headers.ST, rinfo)
}

/**
 * Sends out a response to M-SEARCH commands.
 *
 * @param {String} serviceType Service type requested by a client
 * @param {Object} rinfo Remote client's address
 * @private
 */
SSDP.prototype._respondToSearch = function (serviceType, rinfo) {
  var self = this
    , peer_addr = rinfo.address
    , peer_port = rinfo.port
    , stRegex
    , acceptor

  // unwrap quoted string
  if (serviceType[0] == '"' && serviceType[serviceType.length - 1] == '"') {
    serviceType = serviceType.slice(1, -1)
  }

  if (self._allowWildcards) {
    stRegex = new RegExp(serviceType.replace(/\*/g, '.*') + '$')
    acceptor = function (usn, serviceType) {
      return serviceType === c.SSDP_ALL || stRegex.test(usn)
    }
  } else {
    acceptor = function (usn, serviceType) {
      return serviceType === c.SSDP_ALL || usn === serviceType
    }
  }

  Object.keys(self._usns).forEach(function (usn) {
    var udn = self._usns[usn]

    if (self._allowWildcards) {
      udn = udn.replace(stRegex, serviceType)
    }

    if (acceptor(usn, serviceType)) {
      var header = new SsdpHeader('200 OK', {
        'ST': serviceType === c.SSDP_ALL ? usn : serviceType,
        'USN': udn,
        'CACHE-CONTROL': 'max-age=' + self._ttl,
        'DATE': new Date().toUTCString(),
        'SERVER': self._ssdpSig,
        'EXT': ''
      }, true)

      header.setHeaders(self._extraHeaders)

      if (self._location) {
        header.setHeader('LOCATION', self._location)
      } else {
        header.overrideLocationOnSend()
      }

      self._logger('Sending a 200 OK for an M-SEARCH: %o', {'peer': peer_addr, 'port': peer_port})

      self._send(header, peer_addr, peer_port, function (err, bytes) {
        self._logger('Sent M-SEARCH response: %o', {'message': header.toString(), id: header.id()})
      })
    }
  })
}

/**
 * Parses SSDP response message.
 *
 * @param msg
 * @param rinfo
 */
SSDP.prototype._parseResponse = function parseResponse(msg, rinfo) {
  this._logger('SSDP response: %o', {'message': msg})

  var headers = this._getHeaders(msg)
    , statusCode = this._getStatusCode(msg)

  this.emit('response', headers, statusCode, rinfo)
}

SSDP.prototype.addUSN = function (device) {
  this._usns[device] = this._udn + '::' + device
}

SSDP.prototype._getMethod = function _getMethod(msg) {
  var lines = msg.split("\r\n")
    , type = lines.shift().split(' ')// command, such as "NOTIFY * HTTP/1.1"
    , method = (type[0] || '').toLowerCase()

  return method
}

SSDP.prototype._getStatusCode = function _getStatusCode(msg) {
  var lines = msg.split("\r\n")
    , type = lines.shift().split(' ')// command, such as "NOTIFY * HTTP/1.1"
    , code = parseInt(type[1], 10)

  return code
}

SSDP.prototype._getHeaders = function _getHeaders(msg) {
  var lines = msg.split("\r\n")

  var headers = {}

  lines.forEach(function (line) {
    if (line.length) {
      var pairs = line.match(ssdpHeader)
      if (pairs) headers[pairs[1].toUpperCase()] = pairs[2] // e.g. {'HOST': 239.255.255.250:1900}
    }
  })

  return headers
}

SSDP.prototype._send = function (message, host, port, cb) {
  var self = this

  if (typeof host === 'function') {
    cb = host
    host = this._ssdpIp
    port = this._ssdpPort
  }

  var ipAddresses = Object.keys(this.sockets)

  async.each(ipAddresses, function (ipAddress, next) {
    var socket = self.sockets[ipAddress]
    var buf

    if (message instanceof SsdpHeader) {
      if (message.isOverrideLocationOnSend()) {
        var location = self._locationProtocol + ipAddress + ':' + self._locationPort + self._locationPath
        self._logger('Setting LOCATION header "%s" on message ID %s', location, message.id())
        buf = message.toBuffer({'LOCATION': location})
      } else {
        buf = message.toBuffer()
      }
    } else {
      buf = message
    }

    self._logger('Sending a message to %s:%s', host, port)

    socket.send(buf, 0, buf.length, port, host, next)
  }, cb)
}

function getSsdpSignature() {
  return 'node.js/' + nodeVersion + ' UPnP/1.1 ' + moduleName + '/' + moduleVersion
}

module.exports = SSDP
<commit_msg>Fix uncaught timeout error<commit_after>'use strict'

/*
 MIT License

 Copyright (c) 2016 Ilya Shaisultanov

 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:

 The above copyright notice and this permission notice shall be included in all
 copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */

var dgram = require('dgram')
  , EE = require('events').EventEmitter
  , util = require('util')
  , ip = require('ip')
  , debug = require('debug')
  , os = require('os')
  , async = require('async')
  , extend = require('extend')
  , SsdpHeader = require('./ssdpHeader')

var httpHeader = /HTTP\/\d{1}\.\d{1} \d+ .*/
  , ssdpHeader = /^([^:]+):\s*(.*)$/

/* consts */
var c = require('./const')

var nodeVersion = process.version.substr(1)
  , moduleVersion = require('../package.json').version
  , moduleName = require('../package.json').name

/**
 * Options:
 *
 * @param {Object} opts
 * @param {String} opts.ssdpSig SSDP signature
 * @param {String} opts.ssdpIp SSDP multicast group
 * @param {String} opts.ssdpPort SSDP port
 * @param {Number} opts.ssdpTtl Multicast TTL
 * @param {Number} opts.adInterval Interval at which to send out advertisement (ms)
 * @param {String} opts.description Path to SSDP description file
 * @param {String} opts.udn SSDP Unique Device Name
 * @param {Object} opts.headers Additional headers
 * @param {Array} opts.interfaces Names of interfaces to use. When set, other interfaces are ignored.
 *
 * @param {Number} opts.ttl Packet TTL
 * @param {Boolean} opts.allowWildcards Allow wildcards in M-SEARCH packets (non-standard)
 *
 * @returns {SSDP}
 * @constructor
 */
function SSDP(opts) {
  var self = this

  if (!(this instanceof SSDP)) return new SSDP(opts)

  this._subclass = this._subclass || 'node-ssdp:base'

  opts = opts || {}

  this._logger = opts.customLogger || debug(this._subclass)

  EE.call(self)

  this._init(opts)
}

util.inherits(SSDP, EE)

/**
 * Initializes instance properties.
 * @param opts
 * @private
 */
SSDP.prototype._init = function (opts) {
  this._ssdpSig = opts.ssdpSig || getSsdpSignature()

  this._explicitSocketBind = opts.explicitSocketBind
  this._interfaces = opts.interfaces
  this._reuseAddr = opts.reuseAddr === undefined ? true : opts.reuseAddr

  // User shouldn't need to set these
  this._ssdpIp = opts.ssdpIp || c.SSDP_DEFAULT_IP
  this._ssdpPort = opts.ssdpPort || c.SSDP_DEFAULT_PORT
  this._ssdpTtl = opts.ssdpTtl || 4

  // port on which to listen for messages
  // this generally should be left up to the system for SSDP Client
  // For server, this will be set by the server constructor
  // unless user sets a value.
  this._sourcePort = opts.sourcePort || 0

  this._adInterval = opts.adInterval || 10000

  this._ttl = opts.ttl || 1800

  if (typeof opts.location === 'function') {
    Object.defineProperty(this, '_location', {
      enumerable: true,
      get: opts.location
    })
  } else if (typeof opts.location === 'object') {
    this._locationProtocol = opts.location.protocol || 'http://'
    this._locationPort = opts.location.port
    this._locationPath = opts.location.path
  } else {
    // Probably should specify this explicitly
    this._location = opts.location || 'http://' + ip.address() + ':' + 10293 + '/upnp/desc.html'
  }

  this._ssdpServerHost = this._ssdpIp + ':' + this._ssdpPort

  this._usns = {}
  this._udn = opts.udn || 'uuid:f40c2981-7329-40b7-8b04-27f187aecfb5'

  this._extraHeaders = opts.headers || {}

  this._allowWildcards = opts.allowWildcards

  this._suppressRootDeviceAdvertisements = opts.suppressRootDeviceAdvertisements
}

/**
 * Creates and returns UDP4 socket.
 * Prior to node v0.12.x `dgram.createSocket` did not accept
 * an object and socket reuse was not available; this method
 * contains a crappy workaround to make version of node before 0.12
 * to work correctly. See https://github.com/diversario/node-ssdp/pull/38
 *
 * @returns {Socket}
 * @private
 */
SSDP.prototype._createSockets = function () {
  var interfaces = os.networkInterfaces()
    , self = this

  this.sockets = {}

  Object.keys(interfaces).forEach(function (iName) {
    if (!self._interfaces || self._interfaces.indexOf(iName) > -1) {
      self._logger('discovering all IPs from interface %s', iName)

      interfaces[iName].forEach(function (ipInfo) {
        if (ipInfo.internal == false && ipInfo.family == "IPv4") {
          self._logger('Will use interface %s', iName)
          var socket

          if (parseFloat(process.version.replace(/\w/, '')) >= 0.12) {
            socket = dgram.createSocket({type: 'udp4', reuseAddr: self._reuseAddr})
          } else {
            socket = dgram.createSocket('udp4')
          }

          if (socket) {
            socket.unref()

            self.sockets[ipInfo.address] = socket
          }
        }
      })
    }
  })

  if (Object.keys(this.sockets) == 0) {
    throw new Error('No sockets available, cannot start.')
  }
}

/**
 * Advertise shutdown and close UDP socket.
 */
SSDP.prototype._stop = function () {
  var self = this

  if (!this.sockets) {
    this._logger('Already stopped.')
    return
  }

  Object.keys(this.sockets).forEach(function (ipAddress) {
    var socket = self.sockets[ipAddress]

    socket && socket.close()

    self._logger('Stopped socket on %s', ipAddress)
  })

  this.sockets = null

  this._socketBound = this._started = false
}

/**
 * Configures UDP socket `socket`.
 * Binds event listeners.
 */
SSDP.prototype._start = function (cb) {
  var self = this

  if (self._started) {
    self._logger('Already started.')
    return
  }

  if (!this.sockets) {
    this._createSockets()
  }

  self._started = true

  var interfaces = Object.keys(this.sockets)

  async.each(interfaces, function (iface, next) {
    var socket = self.sockets[iface]

    socket.on('error', function onSocketError(err) {
      self._logger('Socker error: %s', err.message)
    })

    socket.on('message', function onSocketMessage(msg, rinfo) {
      self._parseMessage(msg, rinfo)
    })

    socket.on('listening', function onSocketListening() {
      var addr = socket.address()

      self._logger('SSDP listening: %o', {address: 'http://' + addr.address + ':' + addr.port, 'interface': iface})

      try {
        addMembership()
      } catch (e) {
        if (e.code === 'ENODEV' || e.code === 'EADDRNOTAVAIL') {
          self._logger('Interface %s is not present to add multicast group membership. Scheduling a retry. Error: %s', addr, e.message)
          delay().then(addMembership).catch(e => { throw e })
        } else {
          throw e
        }
      }

      function addMembership() {
        socket.addMembership(self._ssdpIp, iface) // TODO: specifying the interface in there might make a difference
        socket.setMulticastTTL(self._ssdpTtl)
      }

      function delay() {
        return new Promise((resolve) => {
          setTimeout(resolve, 5000);
        });
      }
    })

    if (self._explicitSocketBind) {
      socket.bind(self._sourcePort, iface, next)
    } else {
      socket.bind(self._sourcePort, next) // socket binds on 0.0.0.0
    }
  }, cb)
}

/**
 * Routes a network message to the appropriate handler.
 *
 * @param msg
 * @param rinfo
 */
SSDP.prototype._parseMessage = function (msg, rinfo) {
  msg = msg.toString()

  var type = msg.split('\r\n').shift()

  // HTTP/#.# ### Response to M-SEARCH
  if (httpHeader.test(type)) {
    this._parseResponse(msg, rinfo)
  } else {
    this._parseCommand(msg, rinfo)
  }
}

/**
 * Parses SSDP command.
 *
 * @param msg
 * @param rinfo
 */
SSDP.prototype._parseCommand = function parseCommand(msg, rinfo) {
  var method = this._getMethod(msg)
    , headers = this._getHeaders(msg)

  switch (method) {
    case c.NOTIFY:
      this._notify(headers, msg, rinfo)
      break
    case c.M_SEARCH:
      this._msearch(headers, msg, rinfo)
      break
    default:
      this._logger('Unhandled command: %o', {'message': msg, 'rinfo': rinfo})
  }
}

/**
 * Handles NOTIFY command
 * Emits `advertise-alive`, `advertise-bye` events.
 *
 * @param headers
 * @param msg
 * @param rinfo
 */
SSDP.prototype._notify = function (headers, msg, rinfo) {
  if (!headers.NTS) {
    this._logger('Missing NTS header: %o', headers)
    return
  }

  switch (headers.NTS.toLowerCase()) {
    // Device coming to life.

    case c.SSDP_ALIVE:
      this.emit(c.ADVERTISE_ALIVE, headers, rinfo)
      break

    // Device shutting down.
    case c.SSDP_BYE:
      this.emit(c.ADVERTISE_BYE, headers, rinfo)
      break

    default:
      this._logger('Unhandled NOTIFY event: %o', {'message': msg, 'rinfo': rinfo})
  }
}

/**
 * Handles M-SEARCH command.
 *
 * @param headers
 * @param msg
 * @param rinfo
 */
SSDP.prototype._msearch = function (headers, msg, rinfo) {
  this._logger('SSDP M-SEARCH event: %o', {'ST': headers.ST, 'address': rinfo.address, 'port': rinfo.port})

  if (!headers.MAN || !headers.MX || !headers.ST) return

  this._respondToSearch(headers.ST, rinfo)
}

/**
 * Sends out a response to M-SEARCH commands.
 *
 * @param {String} serviceType Service type requested by a client
 * @param {Object} rinfo Remote client's address
 * @private
 */
SSDP.prototype._respondToSearch = function (serviceType, rinfo) {
  var self = this
    , peer_addr = rinfo.address
    , peer_port = rinfo.port
    , stRegex
    , acceptor

  // unwrap quoted string
  if (serviceType[0] == '"' && serviceType[serviceType.length - 1] == '"') {
    serviceType = serviceType.slice(1, -1)
  }

  if (self._allowWildcards) {
    stRegex = new RegExp(serviceType.replace(/\*/g, '.*') + '$')
    acceptor = function (usn, serviceType) {
      return serviceType === c.SSDP_ALL || stRegex.test(usn)
    }
  } else {
    acceptor = function (usn, serviceType) {
      return serviceType === c.SSDP_ALL || usn === serviceType
    }
  }

  Object.keys(self._usns).forEach(function (usn) {
    var udn = self._usns[usn]

    if (self._allowWildcards) {
      udn = udn.replace(stRegex, serviceType)
    }

    if (acceptor(usn, serviceType)) {
      var header = new SsdpHeader('200 OK', {
        'ST': serviceType === c.SSDP_ALL ? usn : serviceType,
        'USN': udn,
        'CACHE-CONTROL': 'max-age=' + self._ttl,
        'DATE': new Date().toUTCString(),
        'SERVER': self._ssdpSig,
        'EXT': ''
      }, true)

      header.setHeaders(self._extraHeaders)

      if (self._location) {
        header.setHeader('LOCATION', self._location)
      } else {
        header.overrideLocationOnSend()
      }

      self._logger('Sending a 200 OK for an M-SEARCH: %o', {'peer': peer_addr, 'port': peer_port})

      self._send(header, peer_addr, peer_port, function (err, bytes) {
        self._logger('Sent M-SEARCH response: %o', {'message': header.toString(), id: header.id()})
      })
    }
  })
}

/**
 * Parses SSDP response message.
 *
 * @param msg
 * @param rinfo
 */
SSDP.prototype._parseResponse = function parseResponse(msg, rinfo) {
  this._logger('SSDP response: %o', {'message': msg})

  var headers = this._getHeaders(msg)
    , statusCode = this._getStatusCode(msg)

  this.emit('response', headers, statusCode, rinfo)
}

SSDP.prototype.addUSN = function (device) {
  this._usns[device] = this._udn + '::' + device
}

SSDP.prototype._getMethod = function _getMethod(msg) {
  var lines = msg.split("\r\n")
    , type = lines.shift().split(' ')// command, such as "NOTIFY * HTTP/1.1"
    , method = (type[0] || '').toLowerCase()

  return method
}

SSDP.prototype._getStatusCode = function _getStatusCode(msg) {
  var lines = msg.split("\r\n")
    , type = lines.shift().split(' ')// command, such as "NOTIFY * HTTP/1.1"
    , code = parseInt(type[1], 10)

  return code
}

SSDP.prototype._getHeaders = function _getHeaders(msg) {
  var lines = msg.split("\r\n")

  var headers = {}

  lines.forEach(function (line) {
    if (line.length) {
      var pairs = line.match(ssdpHeader)
      if (pairs) headers[pairs[1].toUpperCase()] = pairs[2] // e.g. {'HOST': 239.255.255.250:1900}
    }
  })

  return headers
}

SSDP.prototype._send = function (message, host, port, cb) {
  var self = this

  if (typeof host === 'function') {
    cb = host
    host = this._ssdpIp
    port = this._ssdpPort
  }

  var ipAddresses = Object.keys(this.sockets)

  async.each(ipAddresses, function (ipAddress, next) {
    var socket = self.sockets[ipAddress]
    var buf

    if (message instanceof SsdpHeader) {
      if (message.isOverrideLocationOnSend()) {
        var location = self._locationProtocol + ipAddress + ':' + self._locationPort + self._locationPath
        self._logger('Setting LOCATION header "%s" on message ID %s', location, message.id())
        buf = message.toBuffer({'LOCATION': location})
      } else {
        buf = message.toBuffer()
      }
    } else {
      buf = message
    }

    self._logger('Sending a message to %s:%s', host, port)

    socket.send(buf, 0, buf.length, port, host, next)
  }, cb)
}

function getSsdpSignature() {
  return 'node.js/' + nodeVersion + ' UPnP/1.1 ' + moduleName + '/' + moduleVersion
}

module.exports = SSDP
<|endoftext|>


<commit_before>'use strict'

const serveStatic = require('serve-static')
const connect = require('connect')
const livereload = require('connect-livereload')
const lrserver = require('livereload')
const open = require('open')
const chalk = require('chalk')
const util = require('../util/index')
const getPort = require('get-port')

const exists = util.exists
const resolve = util.resolve

module.exports = function (
  path,
  openInBrowser,
  port,
  livereloadPort,
  indexName
) {
  getPort({port})
    .then(p => {
      port = p
      return getPort({port: livereloadPort})
    })
    .then(p => {
      livereloadPort = p
    })
    .then(_ => {
      path = resolve(path || '.')
      const indexFile = resolve(path, indexName || 'index.html')

      if (!exists(indexFile)) {
        const msg =
          '\nNo docs found ' +
          indexFile +
          '\nPlease run ' +
          chalk.green('docsify init') +
          ' first.\n'
        console.log(msg)
        process.exit(0)
      }

      const server = connect()
      server.use(
        livereload({
          port: livereloadPort
        })
      )
      server.use(serveStatic(path, {index: indexName}))
      server.listen(port)
      lrserver
        .createServer({
          extraExts: ['md'],
          exclusions: ['node_modules/'],
          port: livereloadPort
        })
        .watch(path)

      if (openInBrowser) {
        open(`http://localhost:${port}`)
      }

      const msg =
        '\nServing ' +
        chalk.green(`${path}`) +
        ' now.\n' +
        'Listening at ' +
        chalk.green(`http://localhost:${port}`) +
        '\n'
      console.log(msg)
    })
    .catch(err => {
      console.error(err.message)
    })
}
<commit_msg>refactor: ignore explicit index file reference<commit_after>'use strict'

const serveStatic = require('serve-static')
const connect = require('connect')
const livereload = require('connect-livereload')
const lrserver = require('livereload')
const open = require('open')
const chalk = require('chalk')
const util = require('../util')
const getPort = require('get-port')

const exists = util.exists
const resolve = util.resolve

module.exports = function (
  path,
  openInBrowser,
  port,
  livereloadPort,
  indexName
) {
  getPort({port})
    .then(p => {
      port = p
      return getPort({port: livereloadPort})
    })
    .then(p => {
      livereloadPort = p
    })
    .then(_ => {
      path = resolve(path || '.')
      const indexFile = resolve(path, indexName || 'index.html')

      if (!exists(indexFile)) {
        const msg =
          '\nNo docs found ' +
          indexFile +
          '\nPlease run ' +
          chalk.green('docsify init') +
          ' first.\n'
        console.log(msg)
        process.exit(0)
      }

      const server = connect()
      server.use(
        livereload({
          port: livereloadPort
        })
      )
      server.use(serveStatic(path, {index: indexName}))
      server.listen(port)
      lrserver
        .createServer({
          extraExts: ['md'],
          exclusions: ['node_modules/'],
          port: livereloadPort
        })
        .watch(path)

      if (openInBrowser) {
        open(`http://localhost:${port}`)
      }

      const msg =
        '\nServing ' +
        chalk.green(`${path}`) +
        ' now.\n' +
        'Listening at ' +
        chalk.green(`http://localhost:${port}`) +
        '\n'
      console.log(msg)
    })
    .catch(err => {
      console.error(err.message)
    })
}
<|endoftext|>