<commit_before>define([
    "dojo/_base/declare",
    "dojo/_base/lang",
    "dojo/dom",
    "dojo/dom-construct",
    "dojo/dom-class",
    "dojo/on",
    "esri/geometry/Extent",
    "dojo/window"
],
    function (
        declare,
        lang,
        dom,
        domConstruct,
        domClass,
        on,
        Extent,
        win
    ) {
        return declare("", null, {
            initMapPanel: function () {
                this.mapPanelCSS = {
                    noteContainer: 'note-container',
                    noteItem: 'note-item',
                    noteTitleText: 'note-text',
                    noteContent: 'note-content',
                    notePadding: 'note-padding',
                    noteSelected: 'note-selected',
                    noteImage: 'note-image',
                    noteLink: 'note-link',
                    noteExpand: 'note-expand',
                    noteLoading: 'note-loading',
                    bookmarkItem: 'bookmark-item',
                    clear: 'clear'
                };
                // insert bookmarks
                this._placeBookmarks();
                // insert map notes
                this._placeNotes();
                // description
                if (this.config.enableSummary) {
                    this._setSummary(this.config.summary || this.item.snippet);
                }
            },
            _setSummary: function (description) {
                // map title node
                var node = dom.byId('summary');
                if (node) {
                    // set title
                    node.innerHTML = description;
                }
            },
            _placeNotes: function(){
                // get note layers from the one layer id or title
                this._notesLayers = this._getNotesLayers({
                    map: this.map,
                    layers: this.layers,
                    notesLayer: this.config.notesLayer
                });
                // event for each layer
                this._noteLayerEvents();
                // update note layer title
                this._setNoteLayerTitle();
                // place items to click
                this._placeNoteItems();
            },
            _noteLayerEvent: function(layer){
                // on layer toggle
                on(layer, 'visibility-change', lang.hitch(this, function(){
                    // clear selected feature
                    if(this.map && this.map.infoWindow){
                        this.map.infoWindow.clearFeatures();
                    }
                }));
            },
            _noteLayerEvents: function(){
                // each note event
                if(this._notesLayers.length){
                    for(var i = 0; i < this._notesLayers.length; i++){
                        var layer = this._notesLayers[i];
                        this._noteLayerEvent(layer);
                    }
                }
            },
            _setNoteLayerTitle: function(){
                var node;
                // set title of header to layer title
                if(this._notesLayerTitle && this.noteGraphics && this.noteGraphics.length){
                    node = dom.byId('map_notes_title');
                    if(node){
                        node.innerHTML = this._notesLayerTitle;
                    }
                }
                else{
                    node = dom.byId('map_notes_section');
                    if(node){
                        node.innerHTML = '';
                    }
                }
            },
            _placeNoteItems: function(){
                this.noteNodes = [];
                this.noteGraphics = [];
                this.noteGeometries = [];
                this.noteCount = 0;
                var notesNode = dom.byId('map_notes');
                if(notesNode){
                    // if we have note layers
                    if(this._notesLayers.length){
                        // each note layer
                        for(var i = 0; i < this._notesLayers.length; i++){
                            // hide info window for map notes layers
                            if(this.config.hideNotesLayerPopups){
                                this._notesLayers[i].setInfoTemplate(null);
                            }
                            // get graphics from layer
                            for(var j = 0; j < this._notesLayers[i].graphics.length; j++){
                                if(this._notesLayers[i].graphics && this._notesLayers[i].graphics.length){
                                    // note graphic
                                    var graphic = this._notesLayers[i].graphics[j];
                                    var attributes = this._notesLayers[i].graphics[j].attributes;
                                    var geometry = this._notesLayers[i].graphics[j].geometry;
                                    // save references
                                    this.noteGeometries.push(geometry);
                                    this.noteGraphics.push(graphic);
                                    // note container
                                    var containerNode = domConstruct.create('div', {
                                        className: this.mapPanelCSS.noteContainer
                                    });
                                    // text symbol
                                    if(graphic.symbol && graphic.symbol.type === 'textsymbol'){
                                        attributes.TITLE = graphic.symbol.text;
                                    }
                                    // note title
                                    var titleNode = domConstruct.create('div', {
                                        className: this.mapPanelCSS.noteItem
                                    });
                                    domConstruct.place(titleNode, containerNode, 'last');
                                    // note title
                                    var noteTitleText = domConstruct.create('div', {
                                        innerHTML: attributes.TITLE || this.config.i18n.mapNotes.untitledNote,
                                        className: this.mapPanelCSS.noteTitleText
                                    });
                                    domConstruct.place(noteTitleText, titleNode, 'last');
                                    // note title
                                    var noteExpand = domConstruct.create('div', {
                                        className: this.mapPanelCSS.noteExpand
                                    });
                                    domConstruct.place(noteExpand, titleNode, 'last');
                                    // note title
                                    var clear = domConstruct.create('div', {
                                        className: this.mapPanelCSS.clear
                                    });
                                    domConstruct.place(clear, titleNode, 'last');
                                    // note HTML
                                    var noteContent = '';
                                    if (attributes.DESCRIPTION) {
                                        noteContent = attributes.DESCRIPTION + "\n";
                                    }
                                    // if it has an image
                                    if (attributes.IMAGE_URL) {
                                        // image has link
                                        if (attributes.IMAGE_LINK_URL) {
                                            noteContent += '<image class="' + this.mapPanelCSS.noteImage + '" src= "' + attributes.IMAGE_URL + '" alt="' + attributes.TITLE + '" />';
                                        }
                                        else {
                                            noteContent += '<image class="' + this.mapPanelCSS.noteImage + '" src="' + attributes.IMAGE_URL + '" alt="' + attributes.TITLE + '" />';
                                        }
                                    }
                                    // if no content was set
                                    if(!noteContent){
                                        noteContent = this.config.i18n.mapNotes.notesUnavailable;
                                    }
                                    // note content
                                    var contentNode = domConstruct.create('div', {
                                        className: this.mapPanelCSS.noteContent,
                                        innerHTML: '
' + noteContent + '
'
                                    });
                                    domConstruct.place(contentNode, containerNode, 'last');
                                    // store nodes
                                    this.noteNodes.push({
                                        containerNode: containerNode,
                                        titleNode: titleNode,
                                        noteTitleText: noteTitleText,
                                        noteExpand: noteExpand,
                                        contentNode: contentNode
                                    });
                                    // note event
                                    this._noteEvent(this.noteCount);
                                    // insert node
                                    domConstruct.place(containerNode, notesNode, 'last');
                                    // keep score!
                                    this.noteCount++;
                                }
                            }
                        }
                    }
                }
            },
            // get layer
            _getNotesLayers: function (obj) {
                // get the layer by ID or title
                var mapLayer, mapLayers = [], layers, layer, i, j;
                // if we have a layer id
                if (obj.notesLayer && obj.notesLayer.id) {
                    // note layer id
                    var notesId = obj.notesLayer.id;
                    // todo: May need to remove this at some point
                    var re = /_0$/;
                    // note: removes "_0" from end of ID
                    notesId = notesId.replace(re,"");
                    // each webmap layer
                    for (i = 0; i < obj.layers.length; i++) {
                        layer = obj.layers[i];
                        if (layer.id === notesId) {
                            this._noteLayerObj = layer;
                            this._notesLayerTitle = layer.title;
                            layers = layer.featureCollection.layers;
                            for(j = 0; j < layers.length; j++){
                                mapLayer = obj.map.getLayer(layers[j].id);
                                if(mapLayer){
                                    mapLayers.push(mapLayer);
                                }
                            }
                            break;
                        }
                    }
                }
                return mapLayers;
            },
            _noteEvent: function(idx){
                on(this.noteNodes[idx].titleNode, 'click', lang.hitch(this, function(){
                    // if note open
                    if(domClass.contains(this.noteNodes[idx].containerNode, this.mapPanelCSS.noteSelected)){
                        // close note
                        domClass.toggle(this.noteNodes[idx].containerNode, this.mapPanelCSS.noteSelected);
                        // clear selected feature
                        if(this.map.infoWindow){
                            this.map.infoWindow.clearFeatures();
                        }
                        // dont do any more
                        return;
                    }
                    else{
                        // close selected notes
                        for(var i = 0; i < this.noteNodes.length; i++){
                            domClass.remove(this.noteNodes[i].containerNode, this.mapPanelCSS.noteSelected);
                            // remove any loading
                            domClass.remove(this.noteNodes[i].titleNode, this.mapPanelCSS.noteLoading);
                        }
                        // open note
                        domClass.toggle(this.noteNodes[idx].containerNode, this.mapPanelCSS.noteSelected);
                    }
                    var geometry = this.noteGeometries[idx];
                    var extent;
                    switch(geometry.type){
                        case "point":
                            extent = this.map.extent.centerAt(geometry);
                            break;
                        default:
                            extent = geometry.getExtent();
                    }
                    var vs = win.getBox();
                    if (vs.w < this._showDrawerSize) {
                        this._drawer.toggle().then(lang.hitch(this, function () {
                            // resize map
                            this.map.resize(true);
                            // wait for map to be resized
                            setTimeout(lang.hitch(this, function () {
                                this._setNoteExtent(idx, extent);
                            }), 250);
                        }));
                    } else {
                        this._setNoteExtent(idx, extent);
                    }
                }));
            },
            _turnOnNoteLayers: function(){
                if(this._notesLayers.length){
                    for(var i = 0; i < this._notesLayers.length; i++){
                        var layer = this._notesLayers[i];
                        layer.show();
                    }
                    this._noteLayerObj.visibility = true;
                }
            },
            _setNoteExtent: function(idx, extent){
                this._turnOnNoteLayers();
                domClass.add(this.noteNodes[idx].titleNode, this.mapPanelCSS.noteLoading);
                this.map.setExtent(extent, true).then(lang.hitch(this, function(){
                    // select graphic
                    if(this.map.infoWindow){
                        this.map.infoWindow.set("popupWindow", false);
                        this.map.infoWindow.setFeatures([this.noteGraphics[idx]]);
                        this.map.infoWindow.show(extent.getCenter());
                        this.map.infoWindow.set("popupWindow", true);
                    }
                    domClass.remove(this.noteNodes[idx].titleNode, this.mapPanelCSS.noteLoading);
                }));
            },
            _setBookmarkExtent: function(idx, extent){
                domClass.add(this.bmNodes[idx], this.mapPanelCSS.noteLoading);
                this.map.setExtent(extent).then(lang.hitch(this, function(){
                    domClass.remove(this.bmNodes[idx], this.mapPanelCSS.noteLoading);
                }));
            },
            _bookmarkEvent: function(idx){
                on(this.bmNodes[idx], 'click', lang.hitch(this, function(){
                    // remove any loading
                    for(var i = 0; i < this.bmNodes.length; i++){
                        domClass.remove(this.bmNodes[i], this.mapPanelCSS.noteLoading);
                    }
                    var extent = new Extent(this.bookmarks[idx].extent);
                    var vs = win.getBox();
                    if (vs.w < this._showDrawerSize) {
                        this._drawer.toggle().then(lang.hitch(this, function () {
                            // resize map
                            this.map.resize(true);
                            // wait for map to be resized
                            setTimeout(lang.hitch(this, function () {
                                this._setBookmarkExtent(idx, extent);
                            }), 250);
                        }));
                    } else {
                        this._setBookmarkExtent(idx, extent);
                    }
                }));
            },
            _placeBookmarks: function(){
                var bookmarks = this.bookmarks;
                if (bookmarks && bookmarks.length){
                    var bookmarksNode = dom.byId('map_bookmarks');
                    if(bookmarksNode){
                        this.bmNodes = [];
                        for(var i = 0; i < bookmarks.length; i++){
                            var node = domConstruct.create('div', {
                                innerHTML: bookmarks[i].name,
                                className: this.mapPanelCSS.bookmarkItem
                            });
                            this.bmNodes.push(node);
                            this._bookmarkEvent(i);
                            domConstruct.place(node, bookmarksNode, 'last');
                        }
                    }
                }
            }
        });
    });<commit_msg>fix note showing up<commit_after>define([
    "dojo/_base/declare",
    "dojo/_base/lang",
    "dojo/dom",
    "dojo/dom-construct",
    "dojo/dom-class",
    "dojo/on",
    "esri/geometry/Extent",
    "dojo/window"
],
    function (
        declare,
        lang,
        dom,
        domConstruct,
        domClass,
        on,
        Extent,
        win
    ) {
        return declare("", null, {
            initMapPanel: function () {
                this.mapPanelCSS = {
                    noteContainer: 'note-container',
                    noteItem: 'note-item',
                    noteTitleText: 'note-text',
                    noteContent: 'note-content',
                    notePadding: 'note-padding',
                    noteSelected: 'note-selected',
                    noteImage: 'note-image',
                    noteLink: 'note-link',
                    noteExpand: 'note-expand',
                    noteLoading: 'note-loading',
                    bookmarkItem: 'bookmark-item',
                    clear: 'clear'
                };
                // insert bookmarks
                this._placeBookmarks();
                // insert map notes
                this._placeNotes();
                // description
                if (this.config.enableSummary) {
                    this._setSummary(this.config.summary || this.item.snippet);
                }
            },
            _setSummary: function (description) {
                // map title node
                var node = dom.byId('summary');
                if (node) {
                    // set title
                    node.innerHTML = description;
                }
            },
            _placeNotes: function(){
                // get note layers from the one layer id or title
                this._notesLayers = this._getNotesLayers({
                    map: this.map,
                    layers: this.layers,
                    notesLayer: this.config.notesLayer
                });
                // event for each layer
                this._noteLayerEvents();
                // place items to click
                this._placeNoteItems();
                // update note layer title
                this._setNoteLayerTitle();
            },
            _noteLayerEvent: function(layer){
                // on layer toggle
                on(layer, 'visibility-change', lang.hitch(this, function(){
                    // clear selected feature
                    if(this.map && this.map.infoWindow){
                        this.map.infoWindow.clearFeatures();
                    }
                }));
            },
            _noteLayerEvents: function(){
                // each note event
                if(this._notesLayers.length){
                    for(var i = 0; i < this._notesLayers.length; i++){
                        var layer = this._notesLayers[i];
                        this._noteLayerEvent(layer);
                    }
                }
            },
            _setNoteLayerTitle: function(){
                var node;
                // set title of header to layer title
                if(this._notesLayerTitle && this.noteGraphics && this.noteGraphics.length){
                    node = dom.byId('map_notes_title');
                    if(node){
                        node.innerHTML = this._notesLayerTitle;
                    }
                }
                else{
                    node = dom.byId('map_notes_section');
                    if(node){
                        node.innerHTML = '';
                    }
                }
            },
            _placeNoteItems: function(){
                this.noteNodes = [];
                this.noteGraphics = [];
                this.noteGeometries = [];
                this.noteCount = 0;
                var notesNode = dom.byId('map_notes');
                if(notesNode){
                    // if we have note layers
                    if(this._notesLayers.length){
                        // each note layer
                        for(var i = 0; i < this._notesLayers.length; i++){
                            // hide info window for map notes layers
                            if(this.config.hideNotesLayerPopups){
                                this._notesLayers[i].setInfoTemplate(null);
                            }
                            // get graphics from layer
                            for(var j = 0; j < this._notesLayers[i].graphics.length; j++){
                                if(this._notesLayers[i].graphics && this._notesLayers[i].graphics.length){
                                    // note graphic
                                    var graphic = this._notesLayers[i].graphics[j];
                                    var attributes = this._notesLayers[i].graphics[j].attributes;
                                    var geometry = this._notesLayers[i].graphics[j].geometry;
                                    // save references
                                    this.noteGeometries.push(geometry);
                                    this.noteGraphics.push(graphic);
                                    // note container
                                    var containerNode = domConstruct.create('div', {
                                        className: this.mapPanelCSS.noteContainer
                                    });
                                    // text symbol
                                    if(graphic.symbol && graphic.symbol.type === 'textsymbol'){
                                        attributes.TITLE = graphic.symbol.text;
                                    }
                                    // note title
                                    var titleNode = domConstruct.create('div', {
                                        className: this.mapPanelCSS.noteItem
                                    });
                                    domConstruct.place(titleNode, containerNode, 'last');
                                    // note title
                                    var noteTitleText = domConstruct.create('div', {
                                        innerHTML: attributes.TITLE || this.config.i18n.mapNotes.untitledNote,
                                        className: this.mapPanelCSS.noteTitleText
                                    });
                                    domConstruct.place(noteTitleText, titleNode, 'last');
                                    // note title
                                    var noteExpand = domConstruct.create('div', {
                                        className: this.mapPanelCSS.noteExpand
                                    });
                                    domConstruct.place(noteExpand, titleNode, 'last');
                                    // note title
                                    var clear = domConstruct.create('div', {
                                        className: this.mapPanelCSS.clear
                                    });
                                    domConstruct.place(clear, titleNode, 'last');
                                    // note HTML
                                    var noteContent = '';
                                    if (attributes.DESCRIPTION) {
                                        noteContent = attributes.DESCRIPTION + "\n";
                                    }
                                    // if it has an image
                                    if (attributes.IMAGE_URL) {
                                        // image has link
                                        if (attributes.IMAGE_LINK_URL) {
                                            noteContent += '<image class="' + this.mapPanelCSS.noteImage + '" src= "' + attributes.IMAGE_URL + '" alt="' + attributes.TITLE + '" />';
                                        }
                                        else {
                                            noteContent += '<image class="' + this.mapPanelCSS.noteImage + '" src="' + attributes.IMAGE_URL + '" alt="' + attributes.TITLE + '" />';
                                        }
                                    }
                                    // if no content was set
                                    if(!noteContent){
                                        noteContent = this.config.i18n.mapNotes.notesUnavailable;
                                    }
                                    // note content
                                    var contentNode = domConstruct.create('div', {
                                        className: this.mapPanelCSS.noteContent,
                                        innerHTML: '
' + noteContent + '
'
                                    });
                                    domConstruct.place(contentNode, containerNode, 'last');
                                    // store nodes
                                    this.noteNodes.push({
                                        containerNode: containerNode,
                                        titleNode: titleNode,
                                        noteTitleText: noteTitleText,
                                        noteExpand: noteExpand,
                                        contentNode: contentNode
                                    });
                                    // note event
                                    this._noteEvent(this.noteCount);
                                    // insert node
                                    domConstruct.place(containerNode, notesNode, 'last');
                                    // keep score!
                                    this.noteCount++;
                                }
                            }
                        }
                    }
                }
            },
            // get layer
            _getNotesLayers: function (obj) {
                // get the layer by ID or title
                var mapLayer, mapLayers = [], layers, layer, i, j;
                // if we have a layer id
                if (obj.notesLayer && obj.notesLayer.id) {
                    // note layer id
                    var notesId = obj.notesLayer.id;
                    // todo: May need to remove this at some point
                    var re = /_0$/;
                    // note: removes "_0" from end of ID
                    notesId = notesId.replace(re,"");
                    // each webmap layer
                    for (i = 0; i < obj.layers.length; i++) {
                        layer = obj.layers[i];
                        if (layer.id === notesId) {
                            this._noteLayerObj = layer;
                            this._notesLayerTitle = layer.title;
                            layers = layer.featureCollection.layers;
                            for(j = 0; j < layers.length; j++){
                                mapLayer = obj.map.getLayer(layers[j].id);
                                if(mapLayer){
                                    mapLayers.push(mapLayer);
                                }
                            }
                            break;
                        }
                    }
                }
                return mapLayers;
            },
            _noteEvent: function(idx){
                on(this.noteNodes[idx].titleNode, 'click', lang.hitch(this, function(){
                    // if note open
                    if(domClass.contains(this.noteNodes[idx].containerNode, this.mapPanelCSS.noteSelected)){
                        // close note
                        domClass.toggle(this.noteNodes[idx].containerNode, this.mapPanelCSS.noteSelected);
                        // clear selected feature
                        if(this.map.infoWindow){
                            this.map.infoWindow.clearFeatures();
                        }
                        // dont do any more
                        return;
                    }
                    else{
                        // close selected notes
                        for(var i = 0; i < this.noteNodes.length; i++){
                            domClass.remove(this.noteNodes[i].containerNode, this.mapPanelCSS.noteSelected);
                            // remove any loading
                            domClass.remove(this.noteNodes[i].titleNode, this.mapPanelCSS.noteLoading);
                        }
                        // open note
                        domClass.toggle(this.noteNodes[idx].containerNode, this.mapPanelCSS.noteSelected);
                    }
                    var geometry = this.noteGeometries[idx];
                    var extent;
                    switch(geometry.type){
                        case "point":
                            extent = this.map.extent.centerAt(geometry);
                            break;
                        default:
                            extent = geometry.getExtent();
                    }
                    var vs = win.getBox();
                    if (vs.w < this._showDrawerSize) {
                        this._drawer.toggle().then(lang.hitch(this, function () {
                            // resize map
                            this.map.resize(true);
                            // wait for map to be resized
                            setTimeout(lang.hitch(this, function () {
                                this._setNoteExtent(idx, extent);
                            }), 250);
                        }));
                    } else {
                        this._setNoteExtent(idx, extent);
                    }
                }));
            },
            _turnOnNoteLayers: function(){
                if(this._notesLayers.length){
                    for(var i = 0; i < this._notesLayers.length; i++){
                        var layer = this._notesLayers[i];
                        layer.show();
                    }
                    this._noteLayerObj.visibility = true;
                }
            },
            _setNoteExtent: function(idx, extent){
                this._turnOnNoteLayers();
                domClass.add(this.noteNodes[idx].titleNode, this.mapPanelCSS.noteLoading);
                this.map.setExtent(extent, true).then(lang.hitch(this, function(){
                    // select graphic
                    if(this.map.infoWindow){
                        this.map.infoWindow.set("popupWindow", false);
                        this.map.infoWindow.setFeatures([this.noteGraphics[idx]]);
                        this.map.infoWindow.show(extent.getCenter());
                        this.map.infoWindow.set("popupWindow", true);
                    }
                    domClass.remove(this.noteNodes[idx].titleNode, this.mapPanelCSS.noteLoading);
                }));
            },
            _setBookmarkExtent: function(idx, extent){
                domClass.add(this.bmNodes[idx], this.mapPanelCSS.noteLoading);
                this.map.setExtent(extent).then(lang.hitch(this, function(){
                    domClass.remove(this.bmNodes[idx], this.mapPanelCSS.noteLoading);
                }));
            },
            _bookmarkEvent: function(idx){
                on(this.bmNodes[idx], 'click', lang.hitch(this, function(){
                    // remove any loading
                    for(var i = 0; i < this.bmNodes.length; i++){
                        domClass.remove(this.bmNodes[i], this.mapPanelCSS.noteLoading);
                    }
                    var extent = new Extent(this.bookmarks[idx].extent);
                    var vs = win.getBox();
                    if (vs.w < this._showDrawerSize) {
                        this._drawer.toggle().then(lang.hitch(this, function () {
                            // resize map
                            this.map.resize(true);
                            // wait for map to be resized
                            setTimeout(lang.hitch(this, function () {
                                this._setBookmarkExtent(idx, extent);
                            }), 250);
                        }));
                    } else {
                        this._setBookmarkExtent(idx, extent);
                    }
                }));
            },
            _placeBookmarks: function(){
                var bookmarks = this.bookmarks;
                if (bookmarks && bookmarks.length){
                    var bookmarksNode = dom.byId('map_bookmarks');
                    if(bookmarksNode){
                        this.bmNodes = [];
                        for(var i = 0; i < bookmarks.length; i++){
                            var node = domConstruct.create('div', {
                                innerHTML: bookmarks[i].name,
                                className: this.mapPanelCSS.bookmarkItem
                            });
                            this.bmNodes.push(node);
                            this._bookmarkEvent(i);
                            domConstruct.place(node, bookmarksNode, 'last');
                        }
                    }
                }
            }
        });
    });<|endoftext|>


<commit_before>var _ = require('lodash');
var rmLoc = require('./rmLoc');
var parser = require('./');
var normalizeAST = require('./normalizeASTForTestCompare');

var ruleExample = function(src){
  return function(){
    var ast = parser('ruleset rs{' + src + '}');
    return [src, ast[0].rules[0]];
  };
};

var select_whenExample = function(src){
  return function(){
    var ast = parser('ruleset rs{rule r0{' + src + '}}');
    return [src, ast[0].rules[0].select_when];
  };
};

var examples = {
  '### Ruleset': [
    'ruleset NAME {\n}',
      [
        'ruleset hello {',
        '  meta {',
        '    name "Hello World"',
        '    description <<',
        'Hello parser!',
        '>>',
        '  }',
        '  global {',
        '    a = 1',
        '  }',
        '}'
      ].join('\n')
  ],
  '### Rule': [
    ruleExample('rule NAME {\n}'),
    ruleExample('rule NAME is inactive {\n}'),
    ruleExample([
      'rule hello {',
      '  pre {',
      '    a = 1',
      '  }',
      '',
      '  if COND then',
      '    choose',
      '      one =>',
      '         action(1)',
      '      two =>',
      '         action(2)',
      '',
      '  fired {',
      '    FIRED',
      '  }',
      '}'
    ].join('\n'))
  ],
  '### EventExpression': [
    select_whenExample('select when A B'),
    select_whenExample('select when A B attr re#^(.*)$# setting(val)'),
    select_whenExample('select when A A or B B'),
    select_whenExample('select when any 2 (A A, B B, C C)')
  ],
  '### KRL Expression language': [],
  '#### Literals': [
    '"hello world"',
    '-12.3',
    'thing',
    'true',
    're#^My name is (.*)#i',
    '[A, B, C]',
    '{"one": A}',
    '<<\n  hello #{name}!\n  >>'
  ],
  '#### Assignment': [
    'A = B'
  ],
  '#### Infix Operators': [
    'A && B',
    'A + B + C',
    'A + B * C',
    'A < B',
    'A cmp B',
    'A <=> B'
  ],
  '#### Conditionals': [
    'A => B | C',
    'A => B |\nC => D |\n     E'
  ],
  '#### Functions': [
    'function(A){\n  B\n}',
    'A(B,C)'
  ]
};

var ind = function(n){
  var s = '';
  var i;
  for(i = 0; i < n; i++){
    s += ' ';
  }
  return s;
};

var printAST = function(ast, i, indent_size){
  indent_size = indent_size || 2;
  if(_.isArray(ast)){
    var arr_strs = _.map(ast, function(ast){
      return printAST(ast, i + indent_size, indent_size);
    });
    var flat_array = '[ ' + arr_strs.join(' , ') + ' ]';
    if((flat_array.indexOf('\n') < 0) && (flat_array.length < 20)){
      return flat_array;
    }
    return '[\n'
      + _.map(arr_strs, function(str){
        return ind(i + indent_size) + str;
      }).join(',\n')
      + '\n' + ind(i) + ']';
  }
  if(_.isPlainObject(ast)){
    if(ast.type === 'Identifier' && /^[A-Z]+$/.test(ast.value)){
      return ast.value;
    }
    if(i !== 0 && _.includes(['String', 'Number', 'Boolean', 'Identifier', 'RegExp'], ast.type)){
      var v = ast.type === 'RegExp'
        ? ast.value
        : JSON.stringify(ast.value);
      return '{value: ' + v + ', type:"' + ast.type + '"}';
    }
    return '{\n'
      + _.map(ast, function(value, key){
        var k = JSON.stringify(key);
        var v = printAST(value, i + indent_size, indent_size);
        if(key === 'value' && ast.type === 'RegExp'){
          v = ast.value;
        }
        return ind(i + indent_size) + k + ': ' + v;
      }).join(',\n')
      + '\n' + ind(i) + '}';
  }
  return JSON.stringify(ast);
};

_.each(examples, function(srcs, head){
  console.log();
  console.log(head);
  if(_.isEmpty(srcs)){
    return;
  }
  console.log();
  console.log('```js\n' + _.map(srcs, function(src){
    var ast;
    if(_.isFunction(src)){
      var src_ast = src();
      src = src_ast[0];
      ast = src_ast[1];
    }else{
      ast = parser(src);
    }
    ast = normalizeAST(rmLoc(ast));
    ast = _.isArray(ast) && _.size(ast) === 1 ? _.head(ast) : ast;

    return src + '\n' + printAST(ast, 0, 2);
  }).join('\n\n') + '\n```');
})
<commit_msg>adding select to rule hello example<commit_after>var _ = require('lodash');
var rmLoc = require('./rmLoc');
var parser = require('./');
var normalizeAST = require('./normalizeASTForTestCompare');

var ruleExample = function(src){
  return function(){
    var ast = parser('ruleset rs{' + src + '}');
    return [src, ast[0].rules[0]];
  };
};

var select_whenExample = function(src){
  return function(){
    var ast = parser('ruleset rs{rule r0{' + src + '}}');
    return [src, ast[0].rules[0].select_when];
  };
};

var examples = {
  '### Ruleset': [
    'ruleset NAME {\n}',
      [
        'ruleset hello {',
        '  meta {',
        '    name "Hello World"',
        '    description <<',
        'Hello parser!',
        '>>',
        '  }',
        '  global {',
        '    a = 1',
        '  }',
        '}'
      ].join('\n')
  ],
  '### Rule': [
    ruleExample('rule NAME {\n}'),
    ruleExample('rule NAME is inactive {\n}'),
    ruleExample([
      'rule hello {',
      '  select when DOMAIN TYPE',
      '',
      '  pre {',
      '    a = 1',
      '  }',
      '',
      '  if COND then',
      '    choose',
      '      one =>',
      '         action(1)',
      '      two =>',
      '         action(2)',
      '',
      '  fired {',
      '    FIRED',
      '  }',
      '}'
    ].join('\n'))
  ],
  '### EventExpression': [
    select_whenExample('select when A B'),
    select_whenExample('select when A B attr re#^(.*)$# setting(val)'),
    select_whenExample('select when A A or B B'),
    select_whenExample('select when any 2 (A A, B B, C C)')
  ],
  '### KRL Expression language': [],
  '#### Literals': [
    '"hello world"',
    '-12.3',
    'thing',
    'true',
    're#^My name is (.*)#i',
    '[A, B, C]',
    '{"one": A}',
    '<<\n  hello #{name}!\n  >>'
  ],
  '#### Assignment': [
    'A = B'
  ],
  '#### Infix Operators': [
    'A && B',
    'A + B + C',
    'A + B * C',
    'A < B',
    'A cmp B',
    'A <=> B'
  ],
  '#### Conditionals': [
    'A => B | C',
    'A => B |\nC => D |\n     E'
  ],
  '#### Functions': [
    'function(A){\n  B\n}',
    'A(B,C)'
  ]
};

var ind = function(n){
  var s = '';
  var i;
  for(i = 0; i < n; i++){
    s += ' ';
  }
  return s;
};

var printAST = function(ast, i, indent_size){
  indent_size = indent_size || 2;
  if(_.isArray(ast)){
    var arr_strs = _.map(ast, function(ast){
      return printAST(ast, i + indent_size, indent_size);
    });
    var flat_array = '[ ' + arr_strs.join(' , ') + ' ]';
    if((flat_array.indexOf('\n') < 0) && (flat_array.length < 20)){
      return flat_array;
    }
    return '[\n'
      + _.map(arr_strs, function(str){
        return ind(i + indent_size) + str;
      }).join(',\n')
      + '\n' + ind(i) + ']';
  }
  if(_.isPlainObject(ast)){
    if(ast.type === 'Identifier' && /^[A-Z]+$/.test(ast.value)){
      return ast.value;
    }
    if(i !== 0 && _.includes(['String', 'Number', 'Boolean', 'Identifier', 'RegExp'], ast.type)){
      var v = ast.type === 'RegExp'
        ? ast.value
        : JSON.stringify(ast.value);
      return '{value: ' + v + ', type:"' + ast.type + '"}';
    }
    return '{\n'
      + _.map(ast, function(value, key){
        var k = JSON.stringify(key);
        var v = printAST(value, i + indent_size, indent_size);
        if(key === 'value' && ast.type === 'RegExp'){
          v = ast.value;
        }
        return ind(i + indent_size) + k + ': ' + v;
      }).join(',\n')
      + '\n' + ind(i) + '}';
  }
  return JSON.stringify(ast);
};

_.each(examples, function(srcs, head){
  console.log();
  console.log(head);
  if(_.isEmpty(srcs)){
    return;
  }
  console.log();
  console.log('```js\n' + _.map(srcs, function(src){
    var ast;
    if(_.isFunction(src)){
      var src_ast = src();
      src = src_ast[0];
      ast = src_ast[1];
    }else{
      ast = parser(src);
    }
    ast = normalizeAST(rmLoc(ast));
    ast = _.isArray(ast) && _.size(ast) === 1 ? _.head(ast) : ast;

    return src + '\n' + printAST(ast, 0, 2);
  }).join('\n\n') + '\n```');
})
<|endoftext|>


<commit_before>var providerList = {
    'PEGELONLINE': {
        'name': 'PEGELONLINE',
        'idPrefix': 'po',
        'siteURL': stationsPegelUrl,
        'siteType': 'json',
        'siteParse': processPegelStations,
        'dischargeURL': dischargePegelUrl,
        'dischargeType': 'json',
        'dischargeParse': parsePegelDischarge
    },
    'UKEAengland': {
        'name': 'UKEAengland',
        'idPrefix': 'en',
        'siteURL': stationsUKEAUrl,
        'siteType': 'json',
        'siteParse': processUKEAStations,
        'dischargeURL': dischargeUKEAUrl,
        'dischargeType': 'json',
        'dischargeParse': parseUKEADischarge
    },
    'USGS-DV': {
        'name': 'USGS-DV',
        'idPrefix': 'dv',
        'siteURL': stationsUsgsUrl,
        'siteType': 'text',
        'siteParse': processUsgsStations,
        'dischargeURL': dischargeUsgsUrl,
        'dischargeType': 'json',
        'dischargeParse': parseUsgsDischarge
    },
    'USGS-IV': {
        'name': 'USGS-IV',
        'idPrefix': 'iv',
        'siteURL': stationsUsgsUrl,
        'siteType': 'text',
        'siteParse': processUsgsStations,
        'dischargeURL': dischargeUsgsUrl,
        'dischargeType': 'json',
        'dischargeParse': parseUsgsDischarge
    }
};

function processPegelStations(input) {
    var outJSON = [];
    var csvHeader = 'data:text/csv;charset=utf-8,';
    var outCSV = csvHeader + 'Source,STAID,STANAME,DRAIN_SQKM,HUC02,LAT_GAGE,LNG_GAGE\n';
    input.forEach(function(station, index, array) {
        var tempJSON = {};
        tempJSON['Source'] = 'PEGELONLINE';
        tempJSON['STAID'] = station['number'];
        tempJSON['STANAME'] = station['water']['longname'] + ' at ' + station['longname'];
        tempJSON['LAT_GAGE'] = station['latitude'];
        tempJSON['LNG_GAGE'] = station['longitude'];

        outJSON.push(tempJSON);

        outCSV += 'PEGELONLINE,' + station['number'] + ',' + station['water']['longname'] + ' at ' + station['longname'] + ',null,null,' + station['latitude'] + ',' + station['longitude'] + '\n';
    });
    //console.dir(outJSON);
    return outCSV;
}

function processUKEAStations(input) {
    var outJSON = [];
    var csvHeader = 'data:text/csv;charset=utf-8,';
    var outCSV = csvHeader + 'Source,STAID,STANAME,DRAIN_SQKM,HUC02,LAT_GAGE,LNG_GAGE\n';
    input = input['items'];
    //console.log(input);

    //The site name needs to include the part of the URL that changes. So, it should look like:
    //F1906-flow-logged-i-15_min-m3_s
    //six sites collect more than one type of flow. Taking the first isn't always the best. Still.

    input.forEach(function(station, index, array) {
        var tempJSON = {};
        tempJSON['Source'] = 'UKEAengland';
        //tempJSON['STAID'] = station['stationReference'];
        var stationURL = station['measures'][0]['@id'];
        var id = stationURL.slice(60);
        tempJSON['STAID'] = id;
        var river = station['riverName'] || "";
        if (station['riverName']) river += ' at ';
        var site = station['label'];
        tempJSON['STANAME'] = river + site;
        tempJSON['LAT_GAGE'] = station['lat'];
        tempJSON['LNG_GAGE'] = station['long'];

        outJSON.push(tempJSON);

        //outCSV += 'UKEAengland,' + station['stationReference'] + ',' + river + site + ',null,null,' + station['lat'] + ',' + station['long'] + '\n';
        outCSV += 'UKEAengland,' + id + ',' + river + site + ',null,null,' + station['lat'] + ',' + station['long'] + '\n';
    });
    //console.dir(outJSON);
    return outCSV;
}

function processUsgsStations(input) {
    console.log("processUSGSstations");
    //remove all commas
    var re = /,/gi;
    input = input.replace(re, '');
    //split text at newlines;
    var tempJSON = input.split('\n');
    console.log(input);

    //var outJSON = [];
    var outCSV = '';

    var header = 2;
    tempJSON.forEach(function (line) {
        if (line[0] == '#') {
            //Don't copy lines that start with #.
        } else if (header > 0) {
            //Don't copy the next two lines either.
            header -= 1;
        } else {
            //replace tabs with commas
            var temp = line.split('\t');
            //remove unwanted columns
            //outJSON.push(['USGS', temp[1], temp[2], null, temp[11], temp[4], temp[5]]);
            outCSV += '"USGS", ' + temp[1] + ', ' + temp[2] + ', null, ' + temp[11] + ', ' + temp[4] + ', ' + temp[5] + '\n';
        }
    });

    //add csv header and header row
    var csvHeader = 'data:text/csv;charset=utf-8,';
    outCSV = csvHeader + 'Source,STAID,STANAME,DRAIN_SQKM,HUC02,LAT_GAGE,LNG_GAGE\n' + outCSV;
    return outCSV;
}

function stationsPegelUrl(options) {
    if (options === undefined || options === null) options = {};
    //return 'http://www.pegelonline.wsv.de/webservices/rest-api/v2/stations.json';

    //This returns only stations that measure discharge.
    return 'https://www.pegelonline.wsv.de/webservices/rest-api/v2/stations.json?timeseries=Q&includeTimeseries=true';
}

function stationsUKEAUrl(options) {
    if (options === undefined || options === null) options = {};
    //This seems like the official request. It seems to have the correct header!
    return 'http://environment.data.gov.uk/flood-monitoring/id/stations?parameter=flow';
}

function stationsUsgsUrl(options) {
    if (options === undefined || options === null) options = {};
    var state = options.state || 'al';
    var service = options.service || 'dv';
    var url = 'https://waterservices.usgs.gov/nwis/site/?stateCd=' + state + '&siteStatus=active&parameterCd=00060&outputDataTypeCd=' + service;
    return url;
}

function dischargePegelUrl(site, options) {
    if (options === undefined || options === null) options = {};
    var period = options.period || 'P30D';
    var url = 'http://www.pegelonline.wsv.de/webservices/rest-api/v2/stations/' + site + '/Q/measurements.json?start=' + period;
    return url;
}

function dischargeUKEAUrl(site, options) {
    if (options === undefined || options === null) options = {};
    var period = options.period || 'P30D'; //can't use this. Site only provides data from the past month. Has a since date query though.

    //This query returns all of the data that they are willing to provide for a site. It is limited to the past month, unfortunately.
    var url = 'https://environment.data.gov.uk/flood-monitoring/id/measures/' + site + '/readings?_sorted';
    //var url = 'https://environment.data.gov.uk/flood-monitoring/id/measures/' + site + '-flow--i-15_min-m3_s/readings?_sorted&_limit=' + measurements;
    return url;
}

function dischargeUsgsUrl(site, options) {
    if (options === undefined || options === null) options = {};
    var period = options.period || 'P30D';
    var url = 'https://waterservices.usgs.gov/nwis/dv/?format=json&sites=' + site + '&parameterCd=00060&period=' + period;
    return url;
}

function parsePegelDischarge(returnedData) {
    output = [];
    //check for no data or empty set.
    if (returnedData < 1) {
        return output;
    }
    returnedData.forEach(function (d, index, array) {
        output[index] = [];
        output[index][0] = new Date(d.timestamp);
        //Screen out all negative, '', [], NaN, etc, and store as null.
        output[index][1] = Math.max(0, +d.value) || null;
        //TODO: how does PEGELONLINE data indicate bad values?
    });
    return output;
}

function parseUKEADischarge(returnedData) {
    output = [];
    try {
        var temp = returnedData['items'];
        temp.forEach(function (d, index, array) {
            output[index] = [];
            output[index][0] = new Date(d.dateTime);
            output[index][1] = +d.value;
        });

    } catch (error) {
        console.warn("The UK Environmental Agency did not have data for this site.");
        console.log(error);
    } finally {
        return output;
    }
}

function parseUsgsDischarge(returnedData) {
    output = [];
    //process data
    //    check for no data or empty set;
    try {
        var temp = returnedData.value.timeSeries[0].values[0].value;
        temp.forEach(function (d, index, array) {
            //    convert values to dates and numbers
            output[index] = [];
            output[index][0] = new Date(d.dateTime);
            //screen out negative and non-number values; return null instead.
            // 0 || null returns null.
            var Q = Math.max(0, +d.value * 0.0283168)||null;
            //convert cfs to cms.
            output[index][1] = Q;
            //    a more precise way would be to obtain the actual 'noDataValue'
            //    located at: returnedData.value.timeSeries[0].variable.noDataValue
        });
    } catch (error) {
        console.warn("The USGS did not have data for this site.");
        console.log(error);
    } finally {
        return output;
    }
}

function getDischarge(siteId, source, options) {
    if (options === undefined || options === null) options = {};
    //Get provider-related materials from the providerList
    var provider = providerList[source];
    //My internal site ID has a 2 letter prefix added to the provider's site ID to prevent confusion when two
    // providers use the same site ID.
    var site = siteId.slice(2);
    //console.log("getDischarge siteId: " + siteId + "site: " + site);
    var url = provider.dischargeURL(site, options);
    var data = [];

    //Consider this: What if we checked localStorage here and also integrated old & new data?

    $.ajax({
        url: url,
        dataType: provider.dischargeType,
        error: function (ErrObj, ErrStr) {
            console.warn("error while requesting data!");
            console.log("Data Source: " + provider.name);
            console.log("Requested URL: " + url);
            console.log("Returned Error Object:");
            console.log(ErrObj);
            console.log("Returned Error String:");
            console.log(ErrStr);
            //This seems to give good messages from USGS and PEGELONLINE:
            console.log(ErrObj.statusText);
            //Unless we add the site ID as the element ID, this assumes that the window from this request is still open, and user didn't manage to click another window open quickly!
            $('.googft-info-window#' + siteId).append( "
An error occurred when requesting data for this site.
" );
        },
        success: function (returnedData, statusMsg, returnedjqXHR) {
            console.log("success!");
            console.log(returnedData);
            console.log(statusMsg);
            console.log(returnedjqXHR);
            data = providerList[source].dischargeParse(returnedData);
            //combine the data with whatever is already stored.
            //read data from storage
            var stored = checkStorage(siteId) || [];
            console.log("Stored.length: " + stored.length + "  last: " + end(stored));
            //combine stored with data; concatenate, sort, delete duplicates.
            var newData = joinData(data, stored);
            console.log("just joined data. request.length: " + data.length + " stored.length: " + stored.length + " new.length: " + newData.length);
            data = newData;
        },
        complete: function () {
            console.log('complete!');
            //save the data to localStorage
            //If error, then data = []
            saveData(siteId, data);
            //add data to viewModel.dataArray
            viewModel.dataArray.push(data);

        }
    });

}

function getStations(providerName, options) {
    if (options === undefined || options === null) options = {};
    var provider = providerList[providerName];
    console.log("Get station list from " + provider.name);
    var stationCSV = null;

    $.ajax({
        url: provider.siteURL(options),
        dataType: provider.siteType,
        error: function (ErrObj, ErrStr) {
            console.warn(provider.name + " returned an error.");
            console.dir(ErrObj);
            window.alert(provider.name + " returned an error.\n" + ErrObj.statusText);
        },
        success: function (returnedData, statusMsg, returnedjqXHR) {
            console.log("Success!");

            stationCSV = provider.siteParse(returnedData);

        },
        complete: function () {
            console.log("complete");
            downloadCSV({ filename: provider.name + "-stations.csv", csv: stationCSV });
            //downloadJSON({ filename: provider.name + "-stations.json", json: stationCSV });
        }

    });
}

//Not in use.
function requestStations(providerName, options) {
    if (options === undefined || options === null) options = {};

    var provider = providerList[providerName];
    console.log("Get station list from " + provider.name);
    var stationCSV = null;

    $.ajax({
        url: provider.siteURL,
        dataType: provider.siteType,
        error: function (ErrObj, ErrStr) {
            console.warn(provider.name + " returned an error.");
            console.dir(ErrObj);
            window.alert(provider.name + " returned an error.\n" + ErrObj.statusText);
        },
        success: function (returnedData, statusMsg, returnedjqXHR) {
            console.log("Success!");

            stationCSV = provider.siteParse(returnedData);

        },
        complete: function () {
            console.log("complete");
            downloadCSV({ filename: provider.name + "-stations.csv", csv: stationCSV });
            downloadJSON({ filename: provider.name + "-stations.json", json: stationCSV });
        }

    });
}

<commit_msg>code clean up<commit_after>var providerList = {
    'PEGELONLINE': {
        'name': 'PEGELONLINE',
        'idPrefix': 'po',
        'siteURL': stationsPegelUrl,
        'siteType': 'json',
        'siteParse': processPegelStations,
        'dischargeURL': dischargePegelUrl,
        'dischargeType': 'json',
        'dischargeParse': parsePegelDischarge
    },
    'UKEAengland': {
        'name': 'UKEAengland',
        'idPrefix': 'en',
        'siteURL': stationsUKEAUrl,
        'siteType': 'json',
        'siteParse': processUKEAStations,
        'dischargeURL': dischargeUKEAUrl,
        'dischargeType': 'json',
        'dischargeParse': parseUKEADischarge
    },
    'USGS-DV': {
        'name': 'USGS-DV',
        'idPrefix': 'dv',
        'siteURL': stationsUsgsUrl,
        'siteType': 'text',
        'siteParse': processUsgsStations,
        'dischargeURL': dischargeUsgsUrl,
        'dischargeType': 'json',
        'dischargeParse': parseUsgsDischarge
    },
    'USGS-IV': {
        'name': 'USGS-IV',
        'idPrefix': 'iv',
        'siteURL': stationsUsgsUrl,
        'siteType': 'text',
        'siteParse': processUsgsStations,
        'dischargeURL': dischargeUsgsUrl,
        'dischargeType': 'json',
        'dischargeParse': parseUsgsDischarge
    }
};

function processPegelStations(input) {
    var outJSON = [];
    var csvHeader = 'data:text/csv;charset=utf-8,';
    var outCSV = csvHeader + 'Source,STAID,STANAME,DRAIN_SQKM,HUC02,LAT_GAGE,LNG_GAGE\n';
    input.forEach(function(station, index, array) {
        var tempJSON = {};
        tempJSON['Source'] = 'PEGELONLINE';
        tempJSON['STAID'] = station['number'];
        tempJSON['STANAME'] = station['water']['longname'] + ' at ' + station['longname'];
        tempJSON['LAT_GAGE'] = station['latitude'];
        tempJSON['LNG_GAGE'] = station['longitude'];

        outJSON.push(tempJSON);

        outCSV += 'PEGELONLINE,' + station['number'] + ',' + station['water']['longname'] + ' at ' + station['longname'] + ',null,null,' + station['latitude'] + ',' + station['longitude'] + '\n';
    });
    //console.dir(outJSON);
    return outCSV;
}

function processUKEAStations(input) {
    var outJSON = [];
    var csvHeader = 'data:text/csv;charset=utf-8,';
    var outCSV = csvHeader + 'Source,STAID,STANAME,DRAIN_SQKM,HUC02,LAT_GAGE,LNG_GAGE\n';
    input = input['items'];
    //console.log(input);

    //The site name needs to include the part of the URL that changes. So, it should look like:
    //F1906-flow-logged-i-15_min-m3_s
    //six sites collect more than one type of flow. Taking the first isn't always the best. Still.

    input.forEach(function(station, index, array) {
        var tempJSON = {};
        tempJSON['Source'] = 'UKEAengland';
        //tempJSON['STAID'] = station['stationReference'];
        var stationURL = station['measures'][0]['@id'];
        var id = stationURL.slice(60);
        tempJSON['STAID'] = id;
        var river = station['riverName'] || "";
        if (station['riverName']) river += ' at ';
        var site = station['label'];
        tempJSON['STANAME'] = river + site;
        tempJSON['LAT_GAGE'] = station['lat'];
        tempJSON['LNG_GAGE'] = station['long'];

        outJSON.push(tempJSON);

        //outCSV += 'UKEAengland,' + station['stationReference'] + ',' + river + site + ',null,null,' + station['lat'] + ',' + station['long'] + '\n';
        outCSV += 'UKEAengland,' + id + ',' + river + site + ',null,null,' + station['lat'] + ',' + station['long'] + '\n';
    });
    //console.dir(outJSON);
    return outCSV;
}

function processUsgsStations(input) {
    console.log("processUSGSstations");
    //remove all commas
    var re = /,/gi;
    input = input.replace(re, '');
    //split text at newlines;
    var tempJSON = input.split('\n');
    console.log(input);

    //var outJSON = [];
    var outCSV = '';

    var header = 2;
    tempJSON.forEach(function (line) {
        if (line[0] === '#') {
            //Don't copy lines that start with #.
        } else if (header > 0) {
            //Don't copy the next two lines either.
            header -= 1;
        } else {
            //replace tabs with commas
            var temp = line.split('\t');
            //remove unwanted columns
            //outJSON.push(['USGS', temp[1], temp[2], null, temp[11], temp[4], temp[5]]);
            outCSV += '"USGS", ' + temp[1] + ', ' + temp[2] + ', null, ' + temp[11] + ', ' + temp[4] + ', ' + temp[5] + '\n';
        }
    });

    //add csv header and header row
    var csvHeader = 'data:text/csv;charset=utf-8,';
    outCSV = csvHeader + 'Source,STAID,STANAME,DRAIN_SQKM,HUC02,LAT_GAGE,LNG_GAGE\n' + outCSV;
    return outCSV;
}

function stationsPegelUrl(options) {
    if (options === undefined || options === null) options = {};
    //return 'http://www.pegelonline.wsv.de/webservices/rest-api/v2/stations.json';

    //This returns only stations that measure discharge.
    return 'https://www.pegelonline.wsv.de/webservices/rest-api/v2/stations.json?timeseries=Q&includeTimeseries=true';
}

function stationsUKEAUrl(options) {
    if (options === undefined || options === null) options = {};
    //This seems like the official request. It seems to have the correct header!
    return 'http://environment.data.gov.uk/flood-monitoring/id/stations?parameter=flow';
}

function stationsUsgsUrl(options) {
    if (options === undefined || options === null) options = {};
    var state = options.state || 'al';
    var service = options.service || 'dv';
    var url = 'https://waterservices.usgs.gov/nwis/site/?stateCd=' + state + '&siteStatus=active&parameterCd=00060&outputDataTypeCd=' + service;
    return url;
}

function dischargePegelUrl(site, options) {
    if (options === undefined || options === null) options = {};
    var period = options.period || 'P30D';
    var url = 'http://www.pegelonline.wsv.de/webservices/rest-api/v2/stations/' + site + '/Q/measurements.json?start=' + period;
    return url;
}

function dischargeUKEAUrl(site, options) {
    if (options === undefined || options === null) options = {};
    var period = options.period || 'P30D'; //can't use this. Site only provides data from the past month. Has a since date query though.

    //This query returns all of the data that they are willing to provide for a site. It is limited to the past month, unfortunately.
    var url = 'https://environment.data.gov.uk/flood-monitoring/id/measures/' + site + '/readings?_sorted';
    //var url = 'https://environment.data.gov.uk/flood-monitoring/id/measures/' + site + '-flow--i-15_min-m3_s/readings?_sorted&_limit=' + measurements;
    return url;
}

function dischargeUsgsUrl(site, options) {
    if (options === undefined || options === null) options = {};
    var period = options.period || 'P30D';
    var url = 'https://waterservices.usgs.gov/nwis/dv/?format=json&sites=' + site + '&parameterCd=00060&period=' + period;
    return url;
}

function parsePegelDischarge(returnedData) {
    var output = [];
    //check for no data or empty set.
    if (returnedData < 1) {
        return output;
    }
    returnedData.forEach(function (d, index, array) {
        output[index] = [];
        output[index][0] = new Date(d.timestamp);
        //Screen out all negative, '', [], NaN, etc, and store as null.
        output[index][1] = Math.max(0, +d.value) || null;
        //TODO: how does PEGELONLINE data indicate bad values?
    });
    return output;
}

function parseUKEADischarge(returnedData) {
    var output = [];
    try {
        var temp = returnedData['items'];
        temp.forEach(function (d, index, array) {
            output[index] = [];
            output[index][0] = new Date(d.dateTime);
            output[index][1] = +d.value;
        });

    } catch (error) {
        console.warn("The UK Environmental Agency did not have data for this site.");
        console.log(error);
    } finally {
        return output;
    }
}

function parseUsgsDischarge(returnedData) {
    var output = [];
    //process data
    //    check for no data or empty set;
    try {
        var temp = returnedData.value.timeSeries[0].values[0].value;
        temp.forEach(function (d, index, array) {
            //    convert values to dates and numbers
            output[index] = [];
            output[index][0] = new Date(d.dateTime);
            //screen out negative and non-number values; return null instead.
            // 0 || null returns null.
            var Q = Math.max(0, +d.value * 0.0283168)||null;
            //convert cfs to cms.
            output[index][1] = Q;
            //    a more precise way would be to obtain the actual 'noDataValue'
            //    located at: returnedData.value.timeSeries[0].variable.noDataValue
        });
    } catch (error) {
        console.warn("The USGS did not have data for this site.");
        console.log(error);
    } finally {
        return output;
    }
}

function getDischarge(siteId, source, options) {
    if (options === undefined || options === null) options = {};
    //Get provider-related materials from the providerList
    var provider = providerList[source];
    //My internal site ID has a 2 letter prefix added to the provider's site ID to prevent confusion when two
    // providers use the same site ID.
    var site = siteId.slice(2);
    //console.log("getDischarge siteId: " + siteId + "site: " + site);
    var url = provider.dischargeURL(site, options);
    var data = [];

    //Consider this: What if we checked localStorage here and also integrated old & new data?

    $.ajax({
        url: url,
        dataType: provider.dischargeType,
        error: function (ErrObj, ErrStr) {
            console.warn("error while requesting data!");
            console.log("Data Source: " + provider.name);
            console.log("Requested URL: " + url);
            console.log("Returned Error Object:");
            console.log(ErrObj);
            console.log("Returned Error String:");
            console.log(ErrStr);
            //This seems to give good messages from USGS and PEGELONLINE:
            console.log(ErrObj.statusText);
            //Unless we add the site ID as the element ID, this assumes that the window from this request is still open, and user didn't manage to click another window open quickly!
            $('.googft-info-window#' + siteId).append( "
An error occurred when requesting data for this site.
" );
        },
        success: function (returnedData, statusMsg, returnedjqXHR) {
            console.log("success!");
            console.log(returnedData);
            console.log(statusMsg);
            console.log(returnedjqXHR);
            data = providerList[source].dischargeParse(returnedData);

            //read data from storage
            var stored = checkStorage(siteId) || [];
            console.log("Stored.length: " + stored.length + "  last: " + end(stored));
            //combine stored with data; concatenate, sort, delete duplicates.
            var newData = joinData(data, stored);
            console.log("just joined data. request.length: " + data.length + " stored.length: " + stored.length + " new.length: " + newData.length);
            data = newData;
        },
        complete: function () {
            console.log('complete!');
            //save the data to localStorage
            //If error, then data = []
            saveData(siteId, data);
            //add data to viewModel.dataArray
            viewModel.dataArray.push(data);

        }
    });

}

function getStations(providerName, options) {
    if (options === undefined || options === null) options = {};
    var provider = providerList[providerName];
    console.log("Get station list from " + provider.name);
    var stationCSV = null;

    $.ajax({
        url: provider.siteURL(options),
        dataType: provider.siteType,
        error: function (ErrObj, ErrStr) {
            console.warn(provider.name + " returned an error.");
            console.dir(ErrObj);
            window.alert(provider.name + " returned an error.\n" + ErrObj.statusText);
        },
        success: function (returnedData, statusMsg, returnedjqXHR) {
            console.log("Success!");

            stationCSV = provider.siteParse(returnedData);

        },
        complete: function () {
            console.log("complete");
            downloadCSV({ filename: provider.name + "-stations.csv", csv: stationCSV });
            //downloadJSON({ filename: provider.name + "-stations.json", json: stationCSV });
        }

    });
}

//Not in use.
function requestStations(providerName, options) {
    if (options === undefined || options === null) options = {};

    var provider = providerList[providerName];
    console.log("Get station list from " + provider.name);
    var stationCSV = null;

    $.ajax({
        url: provider.siteURL,
        dataType: provider.siteType,
        error: function (ErrObj, ErrStr) {
            console.warn(provider.name + " returned an error.");
            console.dir(ErrObj);
            window.alert(provider.name + " returned an error.\n" + ErrObj.statusText);
        },
        success: function (returnedData, statusMsg, returnedjqXHR) {
            console.log("Success!");

            stationCSV = provider.siteParse(returnedData);

        },
        complete: function () {
            console.log("complete");
            downloadCSV({ filename: provider.name + "-stations.csv", csv: stationCSV });
            downloadJSON({ filename: provider.name + "-stations.json", json: stationCSV });
        }

    });
}

<|endoftext|>


<commit_before>define(function (require) {
    var activity = require("sugar-web/activity/activity");
    var TWEEN = require("tween");
    require("rAF");

    var maze = require("activity/maze");
    var directions = require("activity/directions");

    require(['domReady!'], function (doc) {
        activity.setup();

        var canvasWidth;
        var canvasHeight;

        var wallColor = "#101010";
        var corridorColor = "#ffffff";
        var startColor = "hsl(0, 0%, 80%)";
        var goalColor;

        var cellWidth;
        var cellHeight;

        var dirtyCells = [];

        var controls = {
            'arrows': [38, 39, 40, 37],
            'wasd': [87, 68, 83, 65],
            'ijkl': [73, 76, 75, 74]
        };

        var controlColors = {};

        var players = {};

        var gameSize = 60;

        var debug = false; //true;

        var mazeCanvas = document.getElementById("maze");
        var ctx = mazeCanvas.getContext("2d");

        var updateMazeSize = function () {
            var toolbarElem = document.getElementById("main-toolbar");

            canvasWidth = window.innerWidth;
            canvasHeight = window.innerHeight - toolbarElem.offsetHeight - 3;

            cellWidth = Math.floor(canvasWidth / maze.width);
            cellHeight = Math.floor(canvasHeight / maze.height);

            mazeCanvas.width = canvasWidth;
            mazeCanvas.height = canvasHeight;
        };

        var onWindowResize = function () {
            updateMazeSize();
            drawMaze();
        };
        window.addEventListener('resize', onWindowResize);

        var drawCell = function (x, y, color) {
            ctx.fillStyle = color;
            ctx.fillRect(cellWidth * x, cellHeight * y, cellWidth, cellHeight);
        }

        var drawGround = function (x, y, value) {
            var color;
            if (value == 1) {
                color = wallColor;
            } else {
                color = corridorColor;
            }
            drawCell(x, y, color);
        };

        var drawPoint = function (x, y, color, size) {
            if (size === undefined) {
                size = 0.5;
            }

            var centerX = cellWidth * (x + 0.5);
            var centerY = cellHeight * (y + 0.5);
            var radius = size * Math.min(cellWidth, cellHeight) / 2;

            ctx.beginPath();
            ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
            ctx.fillStyle = color;
            ctx.fill();
        };

        var drawMazeCell = function (x, y) {
            drawGround(x, y, maze.walls[x][y]);

            if (maze.visited[x][y] !== undefined) {
                drawPoint(x, y, maze.visited[x][y]);
            }

            if (debug) {
                if (maze.forks[x][y] == 1) {
                    drawPoint(x, y, '#faa');
                }
            }

            if (x == maze.startPoint.x && y == maze.startPoint.y) {
                drawPoint(maze.startPoint.x, maze.startPoint.y, startColor, 0.9);
            }

            if (x == maze.goalPoint.x && y == maze.goalPoint.y) {
                drawCell(maze.goalPoint.x, maze.goalPoint.y, goalColor);
            }

            for (control in players) {
                var player = players[control];
                if (x == player.x && y == player.y) {
                    drawPoint(player.x, player.y, player.color, 0.9);
                }
            };

        }

        var drawMaze = function () {
            for (var x=0; x<maze.width; x++) {
                for (var y=0; y<maze.height; y++) {
                    drawGround(x, y, maze.walls[x][y]);
                    if (maze.visited[x][y] !== undefined) {
                        drawPoint(x, y, maze.visited[x][y]);
                    }
                    if (debug) {
                        if (maze.forks[x][y] == 1) {
                            drawPoint(x, y, '#faa');
                        }
                    }
                }
            }

            drawPoint(maze.startPoint.x, maze.startPoint.y, startColor, 0.9);
            drawCell(maze.goalPoint.x, maze.goalPoint.y, goalColor);

            for (control in players) {
                var player = players[control];
                drawPoint(player.x, player.y, player.color, 0.9);
            };

        };

        var runLevel = function () {
            maze.generate(window.innerWidth / window.innerHeight, gameSize);
            updateMazeSize();
            players = {};
            drawMaze();
        }
        runLevel();

        var nextLevel = function () {
            var audio = new Audio('sounds/win.wav');
            audio.play();

            for (control in players) {
                players[control].stop();
            }
            gameSize *= 1.2;
            runLevel();
        }

        var Player = function (control) {
            this.control = control;
            this.x = maze.startPoint.x;
            this.y = maze.startPoint.y;
            if (!(control in controlColors)) {
                var hue = Math.floor(Math.random()*360);
                controlColors[control] = {
                    'color': 'hsl(' + hue + ', 90%, 50%)',
                    'visitedColor': 'hsl(' + hue + ', 30%, 80%)'
                }
            }
            this.color = controlColors[control].color;
            this.visitedColor = controlColors[control].visitedColor;
            this.path = undefined;
            this.animation = undefined;
            this.blockTween = undefined;

            dirtyCells.push({'x': this.x, 'y': this.y});
        };

        Player.prototype.isMoving = function () {
            return (this.animation !== undefined);
        };

        Player.prototype.canGo = function (direction) {
            var dirs = maze.directions[this.x][this.y];
            var i = directions[direction];
            return dirs[i] == 1;
        };

        Player.prototype.findPath = function (direction) {

            var find = function (x, y, direction, first) {

                if (!(first) && (maze.isDeadEnd(x, y) || maze.isFork(x, y))) {
                    return [];
                }

                var nextCell = function (x, y, direction) {
                    var newX = x;
                    var newY = y;
                    var newDir;

                    if (direction == 'north') {
                        newY -= 1;
                    }
                    if (direction == 'east') {
                        newX += 1;
                    }
                    if (direction == 'south') {
                        newY += 1;
                    }
                    if (direction == 'west') {
                        newX -= 1;
                    }

                    var dirs = maze.directions[newX][newY];
                    var tempDirs = dirs.slice(0);
                    tempDirs[directions[directions.getOpposite(direction)]] = 0;
                    newDir = directions.orders[tempDirs.indexOf(1)];

                    return {'x': newX, 'y': newY, 'direction': newDir};
                };

                var next = nextCell(x, y, direction);
                var result = find(next.x, next.y, next.direction, false);
                result.unshift(direction);
                return result;

            };

            return find(this.x, this.y, direction, true);
        }

        Player.prototype.stop = function () {
            clearInterval(this.animation);
            this.animation = undefined;
        }

        Player.prototype.showBlocked = function () {
            var that = this;

            function restoreColor() {
                that.color = controlColors[that.control].color;
            }

            if (this.blockTween !== undefined) {
                this.blockTween.stop();
                restoreColor();
            }

            var hsl = getHSL(this.color);
            var endLight = parseInt(hsl.l.substring(0, hsl.s.length-1));
            var startLight = endLight + 30;

            this.blockTween = new TWEEN.Tween({l: startLight});
            this.blockTween.to({l: endLight}, 300);

            this.blockTween.onUpdate(function () {
                that.color = 'hsl(' + hsl.h + ', ' + hsl.s + ', ' + this.l + '%)';
                dirtyCells.push({'x': that.x, 'y': that.y});
            });

            this.blockTween.onComplete(function () {
                restoreColor();
            });

            this.blockTween.start();

            var audio = new Audio('sounds/tick.wav');
            audio.play();
        }

        Player.prototype.move = function (direction) {
            if (this.isMoving()) {
                return
            }

            if (!(this.canGo(direction))) {
                this.showBlocked();
                return;
            }

            var that = this;

            var next = function () {
                var direction = that.path.shift();
                if (direction == undefined) {
                    that.stop();
                };

                maze.visited[that.x][that.y] = that.visitedColor;

                dirtyCells.push({'x': that.x, 'y': that.y});

                if (direction == 'north') {
                    that.y -= 1;
                }
                if (direction == 'east') {
                    that.x += 1;
                }
                if (direction == 'south') {
                    that.y += 1;
                }
                if (direction == 'west') {
                    that.x -= 1;
                }

                dirtyCells.push({'x': that.x, 'y': that.y});

                if (that.x == maze.goalPoint.x && that.y == maze.goalPoint.y) {
                    nextLevel();
                }
            }

            this.path = this.findPath(direction);
            this.animation = setInterval(next, 40);
        };

        var onKeyDown = function (event) {
            var currentControl;
            var currentDirection;
            for (control in controls) {
                if (controls[control].indexOf(event.keyCode) != -1) {
                    currentControl = control;
                    currentDirection = directions.orders[controls[control].
                                                         indexOf(event.keyCode)];
                }
            }
            if (currentControl === undefined) {
                return;
            }

            if (!(currentControl in players)) {
                players[currentControl] = new Player(currentControl);
            }

            var player = players[currentControl];
            player.move(currentDirection);
        };

        document.addEventListener("keydown", onKeyDown);

        var CSS_INTEGER = "[-\\+]?\\d+%?";
        var CSS_NUMBER = "[-\\+]?\\d*\\.\\d+%?";
        var CSS_UNIT = "(?:" + CSS_NUMBER + ")|(?:" + CSS_INTEGER + ")";
        var MATCH3 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?";
        var hslMatcher = new RegExp("hsl" + MATCH3);

        var getHSL = function (hslColor) {
           var match = hslMatcher.exec(hslColor);
            if (match) {
                return { h: match[1], s: match[2], l: match[3] };
            }
        };

        var animate = function (timestamp) {
            TWEEN.update(timestamp);

            var hue = Math.floor(120 * (1 + Math.cos(timestamp / 3000)));
            var light = Math.floor(50 + (15 * (1 + Math.cos(timestamp / 300))));
            goalColor = 'hsl(' + hue + ', 90%, ' + light + '%)';
            dirtyCells.push({'x': maze.goalPoint.x, 'y': maze.goalPoint.y});

            dirtyCells.forEach(function (cell) {
                drawMazeCell(cell.x, cell.y);
            });
            dirtyCells = [];

            requestAnimationFrame(animate);
        };
        animate();

    });

});
<commit_msg>Add level transition<commit_after>define(function (require) {
    var activity = require("sugar-web/activity/activity");
    var TWEEN = require("tween");
    require("rAF");

    var maze = require("activity/maze");
    var directions = require("activity/directions");

    require(['domReady!'], function (doc) {
        activity.setup();

        var canvasWidth;
        var canvasHeight;

        var wallColor = "#101010";
        var corridorColor = "#ffffff";
        var startColor = "hsl(0, 0%, 80%)";
        var goalColor;

        var cellWidth;
        var cellHeight;

        var dirtyCells = [];

        var controls = {
            'arrows': [38, 39, 40, 37],
            'wasd': [87, 68, 83, 65],
            'ijkl': [73, 76, 75, 74]
        };

        var controlColors = {};

        var players = {};
        var winner;

        var gameSize = 60;

        var onLevelTransition = false;
        var levelTransitionRadius;

        var debug = false; //true;

        var mazeCanvas = document.getElementById("maze");
        var ctx = mazeCanvas.getContext("2d");

        var updateMazeSize = function () {
            var toolbarElem = document.getElementById("main-toolbar");

            canvasWidth = window.innerWidth;
            canvasHeight = window.innerHeight - toolbarElem.offsetHeight - 3;

            cellWidth = Math.floor(canvasWidth / maze.width);
            cellHeight = Math.floor(canvasHeight / maze.height);

            mazeCanvas.width = canvasWidth;
            mazeCanvas.height = canvasHeight;
        };

        var onWindowResize = function () {
            updateMazeSize();
            drawMaze();
        };
        window.addEventListener('resize', onWindowResize);

        var drawCell = function (x, y, color) {
            ctx.fillStyle = color;
            ctx.fillRect(cellWidth * x, cellHeight * y, cellWidth, cellHeight);
        }

        var drawGround = function (x, y, value) {
            var color;
            if (value == 1) {
                color = wallColor;
            } else {
                color = corridorColor;
            }
            drawCell(x, y, color);
        };

        var drawPoint = function (x, y, color, size) {
            if (size === undefined) {
                size = 0.5;
            }

            var centerX = cellWidth * (x + 0.5);
            var centerY = cellHeight * (y + 0.5);
            var radius = size * Math.min(cellWidth, cellHeight) / 2;

            ctx.beginPath();
            ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
            ctx.fillStyle = color;
            ctx.fill();
        };

        var drawMazeCell = function (x, y) {
            drawGround(x, y, maze.walls[x][y]);

            if (maze.visited[x][y] !== undefined) {
                drawPoint(x, y, maze.visited[x][y]);
            }

            if (debug) {
                if (maze.forks[x][y] == 1) {
                    drawPoint(x, y, '#faa');
                }
            }

            if (x == maze.startPoint.x && y == maze.startPoint.y) {
                drawPoint(maze.startPoint.x, maze.startPoint.y, startColor, 0.9);
            }

            if (x == maze.goalPoint.x && y == maze.goalPoint.y) {
                drawCell(maze.goalPoint.x, maze.goalPoint.y, goalColor);
            }

            for (control in players) {
                var player = players[control];
                if (x == player.x && y == player.y) {
                    drawPoint(player.x, player.y, player.color, 0.9);
                }
            };

        }

        var drawMaze = function () {
            for (var x=0; x<maze.width; x++) {
                for (var y=0; y<maze.height; y++) {
                    drawGround(x, y, maze.walls[x][y]);
                    if (maze.visited[x][y] !== undefined) {
                        drawPoint(x, y, maze.visited[x][y]);
                    }
                    if (debug) {
                        if (maze.forks[x][y] == 1) {
                            drawPoint(x, y, '#faa');
                        }
                    }
                }
            }

            drawPoint(maze.startPoint.x, maze.startPoint.y, startColor, 0.9);
            drawCell(maze.goalPoint.x, maze.goalPoint.y, goalColor);

            for (control in players) {
                var player = players[control];
                drawPoint(player.x, player.y, player.color, 0.9);
            };

        };

        var drawLevelComplete = function () {
            var centerX = cellWidth * (winner.x + 0.5);
            var centerY = cellHeight * (winner.y + 0.5);
            var radius = levelTransitionRadius;

            ctx.beginPath();
            ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
            ctx.fillStyle = winner.color;
            ctx.fill();
        }

        var runLevel = function () {
            maze.generate(window.innerWidth / window.innerHeight, gameSize);
            updateMazeSize();
            players = {};
            winner = undefined;
            drawMaze();
        }
        runLevel();

        var onLevelComplete = function (player) {
            winner = player;
            onLevelTransition = true;

            var audio = new Audio('sounds/win.wav');
            audio.play();

            for (control in players) {
                players[control].stop();
            }

            var hypot = Math.sqrt(Math.pow(window.innerWidth, 2) +
                                  Math.pow(window.innerHeight, 2));

            tween = new TWEEN.Tween({radius: 0});
            tween.to({radius: hypot}, 1200);
            tween.easing(TWEEN.Easing.Circular.Out);
            tween.onUpdate(function () {
                levelTransitionRadius = this.radius;
            });
            tween.onComplete(function () {
                onLevelTransition = false;
                nextLevel();
            });
            tween.start();
        }

        var nextLevel = function () {
            gameSize *= 1.2;
            runLevel();
        }

        var Player = function (control) {
            this.control = control;
            this.x = maze.startPoint.x;
            this.y = maze.startPoint.y;
            if (!(control in controlColors)) {
                var hue = Math.floor(Math.random()*360);
                controlColors[control] = {
                    'color': 'hsl(' + hue + ', 90%, 50%)',
                    'visitedColor': 'hsl(' + hue + ', 30%, 80%)'
                }
            }
            this.color = controlColors[control].color;
            this.visitedColor = controlColors[control].visitedColor;
            this.path = undefined;
            this.animation = undefined;
            this.blockTween = undefined;

            dirtyCells.push({'x': this.x, 'y': this.y});
        };

        Player.prototype.isMoving = function () {
            return (this.animation !== undefined);
        };

        Player.prototype.canGo = function (direction) {
            var dirs = maze.directions[this.x][this.y];
            var i = directions[direction];
            return dirs[i] == 1;
        };

        Player.prototype.findPath = function (direction) {

            var find = function (x, y, direction, first) {

                if (!(first) && (maze.isDeadEnd(x, y) || maze.isFork(x, y))) {
                    return [];
                }

                var nextCell = function (x, y, direction) {
                    var newX = x;
                    var newY = y;
                    var newDir;

                    if (direction == 'north') {
                        newY -= 1;
                    }
                    if (direction == 'east') {
                        newX += 1;
                    }
                    if (direction == 'south') {
                        newY += 1;
                    }
                    if (direction == 'west') {
                        newX -= 1;
                    }

                    var dirs = maze.directions[newX][newY];
                    var tempDirs = dirs.slice(0);
                    tempDirs[directions[directions.getOpposite(direction)]] = 0;
                    newDir = directions.orders[tempDirs.indexOf(1)];

                    return {'x': newX, 'y': newY, 'direction': newDir};
                };

                var next = nextCell(x, y, direction);
                var result = find(next.x, next.y, next.direction, false);
                result.unshift(direction);
                return result;

            };

            return find(this.x, this.y, direction, true);
        }

        Player.prototype.stop = function () {
            clearInterval(this.animation);
            this.animation = undefined;
        }

        Player.prototype.showBlocked = function () {
            var that = this;

            function restoreColor() {
                that.color = controlColors[that.control].color;
            }

            if (this.blockTween !== undefined) {
                this.blockTween.stop();
                restoreColor();
            }

            var hsl = getHSL(this.color);
            var endLight = parseInt(hsl.l.substring(0, hsl.s.length-1));
            var startLight = endLight + 30;

            this.blockTween = new TWEEN.Tween({l: startLight});
            this.blockTween.to({l: endLight}, 300);

            this.blockTween.onUpdate(function () {
                that.color = 'hsl(' + hsl.h + ', ' + hsl.s + ', ' + this.l + '%)';
                dirtyCells.push({'x': that.x, 'y': that.y});
            });

            this.blockTween.onComplete(function () {
                restoreColor();
            });

            this.blockTween.start();

            var audio = new Audio('sounds/tick.wav');
            audio.play();
        }

        Player.prototype.move = function (direction) {
            if (this.isMoving()) {
                return
            }

            if (!(this.canGo(direction))) {
                this.showBlocked();
                return;
            }

            var that = this;

            var next = function () {
                var direction = that.path.shift();
                if (direction == undefined) {
                    that.stop();
                };

                maze.visited[that.x][that.y] = that.visitedColor;

                dirtyCells.push({'x': that.x, 'y': that.y});

                if (direction == 'north') {
                    that.y -= 1;
                }
                if (direction == 'east') {
                    that.x += 1;
                }
                if (direction == 'south') {
                    that.y += 1;
                }
                if (direction == 'west') {
                    that.x -= 1;
                }

                dirtyCells.push({'x': that.x, 'y': that.y});

                if (that.x == maze.goalPoint.x && that.y == maze.goalPoint.y) {
                    onLevelComplete(that);
                }
            }

            this.path = this.findPath(direction);
            this.animation = setInterval(next, 40);
        };

        var onKeyDown = function (event) {
            if (onLevelTransition) {
                return;
            }

            var currentControl;
            var currentDirection;
            for (control in controls) {
                if (controls[control].indexOf(event.keyCode) != -1) {
                    currentControl = control;
                    currentDirection = directions.orders[controls[control].
                                                         indexOf(event.keyCode)];
                }
            }
            if (currentControl === undefined) {
                return;
            }

            if (!(currentControl in players)) {
                players[currentControl] = new Player(currentControl);
            }

            var player = players[currentControl];
            player.move(currentDirection);
        };

        document.addEventListener("keydown", onKeyDown);

        var CSS_INTEGER = "[-\\+]?\\d+%?";
        var CSS_NUMBER = "[-\\+]?\\d*\\.\\d+%?";
        var CSS_UNIT = "(?:" + CSS_NUMBER + ")|(?:" + CSS_INTEGER + ")";
        var MATCH3 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?";
        var hslMatcher = new RegExp("hsl" + MATCH3);

        var getHSL = function (hslColor) {
           var match = hslMatcher.exec(hslColor);
            if (match) {
                return { h: match[1], s: match[2], l: match[3] };
            }
        };

        var animateGoal = function (timestamp) {
            var hue = Math.floor(120 * (1 + Math.cos(timestamp / 3000)));
            var light = Math.floor(50 + (10 * (1 + Math.cos(timestamp / 300))));
            goalColor = 'hsl(' + hue + ', 90%, ' + light + '%)';

            dirtyCells.push({'x': maze.goalPoint.x, 'y': maze.goalPoint.y});
        }

        var animate = function (timestamp) {
            TWEEN.update(timestamp);

            if (onLevelTransition) {
                drawLevelComplete();
            } else {
                animateGoal(timestamp);

                dirtyCells.forEach(function (cell) {
                    drawMazeCell(cell.x, cell.y);
                });
                dirtyCells = [];
            }

            requestAnimationFrame(animate);
        };
        animate();

    });

});
<|endoftext|>