<commit_before>/**
 * Copyright 2014 Solinea, Inc.
 *
 * Licensed under the Solinea Software License Agreement (goldstone),
 * Version 1.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *     http://www.solinea.com/goldstone/LICENSE.pdf
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Author: Alex Jacobs
 */

var ServiceStatusView = Backbone.View.extend({

    defaults: {},

    initialize: function(options) {
        this.options = options || {};
        this.defaults = _.clone(this.defaults);
        this.defaults.url = this.collection.url;
        this.el = options.el;
        this.defaults.width = options.width;

        var ns = this.defaults;
        var self = this;

        // required in case spinner loading takes
        // longer than chart loading
        ns.spinnerDisplay = 'inline';

        var appendSpinnerLocation = ns.location;
        $('').load(function() {
            $(this).appendTo(appendSpinnerLocation).css({
                'position': 'relative',
                'margin-left': (ns.width / 2),
                'display': ns.spinnerDisplay
            });
        });

        this.collection.on('sync', function() {
            if (self.collection.defaults.setAchieved) {
                console.log('final set at sync: ', self.collection.length, self.collection.toJSON());

                self.update();

                self.collection.defaults.fetchInProgress = false;
                console.log('fetchInProgress?', self.collection.defaults.fetchInProgress);
                self.collection.defaults.setAchieved = false;
            }
        });
    },

    classSelector: function(item) {
        if (item === "running") {
            return 'alert alert-success';
        }
        return 'alert alert-danger fa fa-exclamation-circle';
    },

    collectionPrep: function() {
        var ns = this.defaults;
        var self = this;

        allthelogs = this.collection.toJSON();

        var data = allthelogs;
        console.log('just arriving in collectionPrep; len=', data.length);

        /*
        iterate through 'data, find the index at which point a duplicate appears, and truncate (slice) the array at that point.
        massage the array to be a simple object {name:true/false} and return it
        */

        var initialSetConstruction = {};

        for (var i = 0; i < data.length; i++) {

            var service = data[i].name;

            if (initialSetConstruction[service]) {
                console.log('dupe found at', i, data.length);
                data = data.slice(0, i);
                break;
            }

            initialSetConstruction[service] = true;
        }

        console.log('data.len before removing dupes = ', data.length);

        var finalData = [];

        /*
        figure out here how to push the object name as
        */
        _.each(data, function(item) {
            var resultObject = {};
            var resultName = item.name.slice(item.name.lastIndexOf('.') + 1);
            // console.log(resultName);
            resultObject[resultName] = item.value;
            finalData.push(resultObject);
        });
        console.log('final data.len after removing dupes = ', finalData.length);

        return finalData;

    },

    update: function() {

        var ns = this.defaults;
        var self = this;

        // sets css for spinner to hidden in case
        // spinner callback resolves
        // after chart data callback
        ns.spinnerDisplay = 'none';
        $(this.el).find('#spinner').hide();

        this.render();

        var allthelogs = this.collectionPrep();

        console.log('allthelogs from in update', allthelogs.length, allthelogs);

        // refreshes every 30 seconds
        setTimeout(function() {
            self.collection.retrieveData();
        }, 30000);

        // If we didn't receive any valid files, append "No Data Returned"
        if (allthelogs.length === 0) {

            // if 'no data returned' already exists on page, don't reapply it
            if ($(this.el).find('#noDataReturned').length) {
                return;
            }

            $('No Data Returned').appendTo(this.el)
                .css({
                    'position': 'relative',
                    'margin-left': $(this.el).width() / 2 - 14,
                    'top': -$(this.el).height() / 2
                });
            return;
        }

        // remove No Data Returned once data starts flowing again
        if ($(this.el).find('#noDataReturned').length) {
            $(this.el).find('#noDataReturned').remove();
        }

        var nodeNames = [];

        _.each(allthelogs, function(item) {
            nodeNames.push(item);
        });

        this.sorter(nodeNames);

        _.each(nodeNames, function(item, i) {

            var itemKeyFull = '';
            var itemValue = _.values(nodeNames[i])[0];
            var itemKey = _.keys(nodeNames[i])[0];
            if (itemKey.length > 27) {
                itemKeyFull = _.keys(nodeNames[i])[0];
                itemKey = itemKey.slice(0, 27) + '...';
            }
            console.log(itemKey, itemValue);

            $(self.el).find('.mainContainer').append('
 ' + itemKey + '
');
        }, this);

        $(this.el).find('.mainContainer .toRemove').on('mouseover', function() {
            $(this).tooltip('show');
        });
    },

    sorter: function(data) {

        return data.sort(function(a, b) {
            if (Object.keys(a) < Object.keys(b)) {
                return -1;
            }
            if (Object.keys(a) > Object.keys(b)) {
                return 1;
            } else {
                return 0;
            }
        });

    },

    render: function() {

        if ($(this.el).find('.mainContainer').length === 0) {
            this.$el.append(this.template());
            return this;
        } else {
            $(this.el).find('.mainContainer').empty();
        }

    },

    template: _.template("

")

});
<commit_msg>Improve logic for serviceStatus name extraction, and remove pile-up of click-listeners.<commit_after>/**
 * Copyright 2014 Solinea, Inc.
 *
 * Licensed under the Solinea Software License Agreement (goldstone),
 * Version 1.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *     http://www.solinea.com/goldstone/LICENSE.pdf
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Author: Alex Jacobs
 */

var ServiceStatusView = Backbone.View.extend({

    defaults: {},

    initialize: function(options) {
        this.options = options || {};
        this.defaults = _.clone(this.defaults);
        this.defaults.url = this.collection.url;
        this.el = options.el;
        this.defaults.width = options.width;

        var ns = this.defaults;
        var self = this;

        // required in case spinner loading takes
        // longer than chart loading
        ns.spinnerDisplay = 'inline';

        var appendSpinnerLocation = ns.location;
        $('').load(function() {
            $(this).appendTo(appendSpinnerLocation).css({
                'position': 'relative',
                'margin-left': (ns.width / 2),
                'display': ns.spinnerDisplay
            });
        });

        this.collection.on('sync', function() {
            if (self.collection.defaults.setAchieved) {
                console.log('final set at sync: ', self.collection.length, self.collection.toJSON());

                self.update();

                self.collection.defaults.fetchInProgress = false;
                console.log('fetchInProgress?', self.collection.defaults.fetchInProgress);
                self.collection.defaults.setAchieved = false;
            }
        });
    },

    classSelector: function(item) {
        if (item === "running") {
            return 'alert alert-success';
        }
        return 'alert alert-danger fa fa-exclamation-circle';
    },

    collectionPrep: function() {
        var ns = this.defaults;
        var self = this;

        allthelogs = this.collection.toJSON();

        var data = allthelogs;
        console.log('just arriving in collectionPrep; len=', data.length);

        /*
        iterate through 'data, find the index at which point a duplicate appears, and truncate (slice) the array at that point.
        massage the array to be a simple object {name:true/false} and return it
        */

        var initialSetConstruction = {};

        for (var i = 0; i < data.length; i++) {

            var service = data[i].name;

            if (initialSetConstruction[service]) {
                console.log('dupe found at', i, data.length);
                data = data.slice(0, i);
                break;
            }

            initialSetConstruction[service] = true;
        }

        console.log('data.len before removing dupes = ', data.length);

        var finalData = [];

        _.each(data, function(item) {
            var resultName;
            var resultObject = {};
            if(item.name && item.name.indexOf('.') !== -1){
                resultName = item.name.slice(item.name.lastIndexOf('.') + 1);
            } else {
                resultName = item.name;
            }
            // console.log(resultName);
            resultObject[resultName] = item.value;
            finalData.push(resultObject);
        });
        console.log('final data.len after removing dupes = ', finalData.length);

        return finalData;

    },

    update: function() {

        var ns = this.defaults;
        var self = this;

        // sets css for spinner to hidden in case
        // spinner callback resolves
        // after chart data callback
        ns.spinnerDisplay = 'none';
        $(this.el).find('#spinner').hide();

        this.render();

        var allthelogs = this.collectionPrep();

        console.log('allthelogs from in update', allthelogs.length, allthelogs);

        // refreshes every 30 seconds
        setTimeout(function() {
            self.collection.retrieveData();
        }, 30000);

        // If we didn't receive any valid files, append "No Data Returned"
        if (allthelogs.length === 0) {

            // if 'no data returned' already exists on page, don't reapply it
            if ($(this.el).find('#noDataReturned').length) {
                return;
            }

            $('No Data Returned').appendTo(this.el)
                .css({
                    'position': 'relative',
                    'margin-left': $(this.el).width() / 2 - 14,
                    'top': -$(this.el).height() / 2
                });
            return;
        }

        // remove No Data Returned once data starts flowing again
        if ($(this.el).find('#noDataReturned').length) {
            $(this.el).find('#noDataReturned').remove();
        }

        var nodeNames = [];

        _.each(allthelogs, function(item) {
            nodeNames.push(item);
        });

        this.sorter(nodeNames);

        _.each(nodeNames, function(item, i) {

            var itemKeyFull = '';
            var itemValue = _.values(nodeNames[i])[0];
            var itemKey = _.keys(nodeNames[i])[0];
            if (itemKey.length > 27) {
                itemKeyFull = _.keys(nodeNames[i])[0];
                itemKey = itemKey.slice(0, 27) + '...';
            }
            console.log(itemKey, itemValue);

            $(self.el).find('.mainContainer').append('
 ' + itemKey + '
');
        }, this);

        $(this.el).find('.mainContainer .toRemove').on('mouseover', function() {
            $(this).tooltip('show');
        });
    },

    sorter: function(data) {

        return data.sort(function(a, b) {
            if (Object.keys(a) < Object.keys(b)) {
                return -1;
            }
            if (Object.keys(a) > Object.keys(b)) {
                return 1;
            } else {
                return 0;
            }
        });

    },

    render: function() {

        if ($(this.el).find('.mainContainer').length === 0) {
            this.$el.append(this.template());
            return this;
        } else {
            // remove hover listeners from service status nodes
            $(this.el).find('.mainContainer .toRemove').off();
            $(this.el).find('.mainContainer').empty();
        }

    },

    template: _.template("

")

});
<|endoftext|>


<commit_before>// Requires cors.js to be loaded first

"use strict";

var ZOTERO_CONFIG = {
   "zotId": "2211939", // ID of group or user library to search in Zotero, e.g., 2211939, 2055673
   "zotIdType": "group", // group or user
   "collectionKey": "", // Key of collection within library to search, e.g., "KHTHLKB5", or "" if no collection
   "filterTags": "", // For filtering results by tag(s), e.g., "&tag=LTER-Funded".  See examples at https://www.zotero.org/support/dev/web_api/v3/basics
   "resultsElementId": "searchResults", // Element to contain results
   "includeCols": ["Year", "Type", "ShowTags"], // Array of columns to include in the output table, other than Citation. The full set is ["Year", "Type", "ShowTags"]
   "showTags": ["Foundational", "LTER-Funded", "LTER-Enabled"], // Include a column showing this tag if present for each item
   "showTagColName": "Relationship", // Name for the column in HTML table under which the showTags will appear
   "style": "", // Bibliography display style, e.g., apa. Leave blank for default which is chicago-note-bibliography.
   "limit": 10, // Max number of results to retrieve per page
   "urlElementId": "searchUrl", // Element to display search URL
   "countElementId": "resultCount", // Element showing number of results
   "pagesTopElementId": "paginationTop", // Element to display result page links above results
   "pagesBotElementId": "paginationBot", // Element to display result page links below results
   "showPages": 5, // MUST BE ODD NUMBER! Max number of page links to show
   "sortDiv": "sortDiv" // Element with interactive sort options
};

// Get URL arguments
function getParameterByName(name, url) {
   if (!url) url = window.location.href;
   name = name.replace(/[\[\]]/g, "\\$&");
   var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
      results = regex.exec(url);
   if (!results) return null;
   if (!results[2]) return "";
   return decodeURIComponent(results[2].replace(/\+/g, " "));
}

// Parse Zotero search results into HTML
function parseZoteroResults(resultText) {
   function parseYear(text) {
      function isYear(yearText) {
         // Assumes year is between 1000 and current year + 5
         try {
            var yr = parseInt(yearText);
            var maxAllowable = (new Date().getFullYear()) + 5;
            return (String(yr) === yearText && yr >= 1000 && yr <= maxAllowable)
         } catch (err) {
            return false;
         }
      }

      if (text) {
         if (isYear(text.slice(0, 4))) {
            return text.slice(0, 4);
         } else if (isYear(text.slice(-4))) {
            return text.slice(-4);
         } else {
            return text;
         }
      } else {
         return "";
      }
   }

   function parseType(text) {
      switch (text) {
         case "artwork":
            return "Artwork";
         case "audioRecording":
            return "Audio Recording";
         case "bill":
            return "Bill";
         case "blogPost":
            return "Blog Post";
         case "book":
            return "Book";
         case "bookSection":
            return "Book Section";
         case "case":
            return "Case";
         case "computerProgram":
            return "Computer Program";
         case "conferencePaper":
            return "In Proceedings";
         case "dictionaryEntry":
            return "Dictionary Entry";
         case "document":
            return "Document";
         case "email":
            return "Email";
         case "encyclopediaArticle":
            return "Encyclopedia Article";
         case "film":
            return "Film";
         case "forumPost":
            return "Forum Post";
         case "hearing":
            return "Hearing";
         case "instantMessage":
            return "Instant Message";
         case "interview":
            return "Interview";
         case "journalArticle":
            return "Journal Article";
         case "letter":
            return "Letter";
         case "magazineArticle":
            return "Magazine Article";
         case "manuscript":
            return "Manuscript";
         case "map":
            return "Map";
         case "newspaperArticle":
            return "Newspaper Article";
         case "patent":
            return "Patent";
         case "podcast":
            return "Podcast";
         case "presentation":
            return "Presentation";
         case "radioBroadcast":
            return "Radio Broadcast";
         case "report":
            return "Report";
         case "statute":
            return "Statute";
         case "thesis":
            return "Thesis";
         case "tvBroadcast":
            return "TV Broadcast";
         case "videoRecording":
            return "Video Recording";
         case "webpage":
            return "Web Page";
         case "note":
            return "Note";
         default:
            return "Other"
      }
   }

   function parseShowTags(tags) {
      function objToString(tagObj) {
         return tagObj["tag"];
      }
      var tagArray = tags.map(objToString);
      var matches = tagArray.filter(function (n) {
         return ZOTERO_CONFIG["showTags"].indexOf(n) !== -1;
      });
      return matches.join(", ");
   }

   var results = JSON.parse(resultText);
   var sortDiv = document.getElementById(ZOTERO_CONFIG["sortDiv"]);
   if (sortDiv) {
      if (results.length)
         sortDiv.style.display = "block";
      else
         sortDiv.style.display = "none";
   }
   if (!results.length) {
      return "
Your search returned no results.
";
   }

   var header = '

';
   var showYear = (ZOTERO_CONFIG["includeCols"].indexOf("Year") !== -1);
   var showType = (ZOTERO_CONFIG["includeCols"].indexOf("Type") !== -1);
   var showTags = (ZOTERO_CONFIG["includeCols"].indexOf("ShowTags") !== -1);
   if (ZOTERO_CONFIG["includeCols"].length > 0) {
      header += "

";
      if (showYear) {
         header += "Year	";
      }
      header += "Citation	";
      if (showType) {
         header += "Type	";
      }
      if (showTags) {
         header += "" + ZOTERO_CONFIG["showTagColName"] + "	";
      }
      header += "

";
   }

   var rows = [header];

   for (var i = 0; i < results.length; i++) {
      var result = results[i];
      var year = parseYear(result["data"]["date"]);
      var itemType = parseType(result["data"]["itemType"]);
      var tagsToShow = parseShowTags(result["data"]["tags"]);
      var row = "
";
      if (showYear) {
         row += "" + year + "	";
      }
      row += "" + result["bib"] + "	";
      if (showType) {
         row += "" + itemType + "	";
      }
      if (showTags) {
         row += "" + tagsToShow + "	";
      }
      row += "
"
      rows.push(row);
   }
   rows.push("

");
   return rows.join("");
}

function showLoading(isLoading) {
   var x = document.getElementById("loading-div");
   if (isLoading) {
      document.body.style.cursor = "wait";
      x.style.display = "block";
   } else {
      document.body.style.cursor = "default";
      x.style.display = "none";
   }
}

// Function to call if CORS request is successful
function successCallback(headers, response) {
   showLoading(false);

   // Write results to page
   var resultHtml = parseZoteroResults(response);
   var elementId = ZOTERO_CONFIG["resultsElementId"];
   document.getElementById(elementId).innerHTML = resultHtml;

   // Add links to additional search result pages if necessary
   var currentStart = getParameterByName("start");
   if (!currentStart) {
      currentStart = 0;
   } else {
      currentStart = parseInt(currentStart);
   }
   var count = parseInt(headers["total-results"]);
   var limit = parseInt(ZOTERO_CONFIG["limit"]);
   var showPages = parseInt(ZOTERO_CONFIG["showPages"]);
   var pageTopElementId = ZOTERO_CONFIG["pagesTopElementId"];
   var pageBotElementId = ZOTERO_CONFIG["pagesBotElementId"];
   showPageLinks(count, limit, showPages, currentStart, pageTopElementId);
   showPageLinks(count, limit, showPages, currentStart, pageBotElementId);
   var query = getParameterByName("q");
   showResultCount(query, count, limit, currentStart, ZOTERO_CONFIG["countElementId"]);
}

// Function to call if CORS request fails
function errorCallback() {
   showLoading(false);
   alert("There was an error making the request.");
}

// Writes CORS request URL to the page so user can see it
function showUrl(url) {
   var element = document.getElementById(ZOTERO_CONFIG["urlElementId"]);
   if (!element) return;
   var txt = '' + url + '';
   element.innerHTML = txt;
}

// Passes search URL and callbacks to CORS function
function searchZotero(query, itemType, sort, start) {
   var zotId = (ZOTERO_CONFIG["zotIdType"] === "group") ? "groups/" + ZOTERO_CONFIG["zotId"] : "users/" + ZOTERO_CONFIG["zotId"];
   var collection = (ZOTERO_CONFIG["collectionKey"] === "") ? "/" : "/collections/" + ZOTERO_CONFIG["collectionKey"] + "/";
   var base = "https://api.zotero.org/" + zotId + collection + "/items?v=3&include=bib,data";
   var style = (ZOTERO_CONFIG["style"] === "") ? "" : "&style=" + ZOTERO_CONFIG["style"];
   var params = "&q=" + encodeURI(query) + "&itemType=" + itemType +
      "&sort=" + sort + "&start=" + start + ZOTERO_CONFIG["filterTags"];
   var limit = "&limit=" + ZOTERO_CONFIG["limit"];
   var url = base + params + style + limit;
   showUrl(url);
   showLoading(true);
   makeCorsRequest(url, successCallback, errorCallback);
}

function initCollapsible(expanded) {
   // Handles collapsible sections
   function showHide(el, show) {
      if (show) el.style.maxHeight = "900px";
      else el.style.maxHeight = null;
   }

   // Expand if user tabs into hidden element
   function listenForFocus(collapsibleEl, toggle) {
      function addFocusListener(collapsibleEl, tagName, toggle) {
         var els = collapsibleEl.getElementsByTagName(tagName);
         var i;
         for (i = 0; i < els.length; i++) {
            els[i].onfocus = function () {
               if (!toggle.checked) toggle.click();
            };
         };
      }
      addFocusListener(collapsibleEl, "SELECT", toggle);
      addFocusListener(collapsibleEl, "INPUT", toggle);
   }

   // Collapse when checked
   var els = document.getElementsByClassName("collapse-toggle");
   var i;
   for (i = 0; i < els.length; i++) {
      var el = els[i];
      if (el.type && el.type === 'checkbox') {
         el.checked = expanded;
         var target = document.getElementById(el.getAttribute("aria-controls"));
         listenForFocus(target, el);
         showHide(target, expanded);
         el.setAttribute("aria-expanded", expanded.toString());
         el.onchange = function () {
            showHide(target, this.checked);
            this.setAttribute("aria-expanded", this.checked.toString());
         };
      }
   }
   // Toggle checkbox when user presses space or enter on label
   els = document.getElementsByClassName("lbl-toggle");
   for (i = 0; i < els.length; i++) {
      var label = els[i];
      label.onkeydown = function (e) {
         if (e.which === 32 || e.which === 13) {
            e.preventDefault();
            this.click();
         };
      };
   };
}

function initForm(formId, expanded) {
   initCollapsible(expanded);

   var sortControl = document.getElementById("visibleSort");
   if (sortControl) {
      sortControl.onchange = function () {
         var hiddenSortControl = document.getElementById("sort");
         hiddenSortControl.value = this.options[this.selectedIndex].value;
         var form = document.getElementById(formId);
         form.submit();
      };
   }
}

// Selects the desired value in the Select control. If value is not in the
// control, then first index is used. Returns actual selected value.  If
// control is not present, input value is returned.
function setSelectValue(elId, desiredValue) {
   var el = document.getElementById(elId);
   if (!el || !el.length) return desiredValue;
   var result = el[0].value;
   for (var i = 0; i < el.length; i++) {
      if (desiredValue === el[i].value) {
         el[i].selected = true;
         result = desiredValue;
         break;
      }
   }
   return result;
}

// When the window loads, read query parameters and perform search
window.onload = function () {
   var query = getParameterByName("q") || "";
   var itemTypeParam = getParameterByName("itemType") || "-attachment";
   var expanded = Boolean(getParameterByName("expanded"));
   var pageStart = getParameterByName("start") || 0;
   var sortParam = getParameterByName("sort") || "date";

   document.forms.zoteroSearchForm.q.value = query;
   var itemType = setSelectValue("itemType", itemTypeParam);
   var sort = setSelectValue("visibleSort", sortParam);
   var sortHiddenInput = document.getElementById("sort");
   if (sortHiddenInput) {
      document.forms.zoteroSearchForm.sort.value = sort;
   }

   initForm("zoteroSearchForm", expanded);

   searchZotero(query, itemType, sort, pageStart);
};<commit_msg>Parse issueDate (for patents) if no date<commit_after>// Requires cors.js to be loaded first

"use strict";

var ZOTERO_CONFIG = {
   "zotId": "2211939", // ID of group or user library to search in Zotero, e.g., 2211939, 2055673
   "zotIdType": "group", // group or user
   "collectionKey": "", // Key of collection within library to search, e.g., "KHTHLKB5", or "" if no collection
   "filterTags": "", // For filtering results by tag(s), e.g., "&tag=LTER-Funded".  See examples at https://www.zotero.org/support/dev/web_api/v3/basics
   "resultsElementId": "searchResults", // Element to contain results
   "includeCols": ["Year", "Type", "ShowTags"], // Array of columns to include in the output table, other than Citation. The full set is ["Year", "Type", "ShowTags"]
   "showTags": ["Foundational", "LTER-Funded", "LTER-Enabled"], // Include a column showing this tag if present for each item
   "showTagColName": "Relationship", // Name for the column in HTML table under which the showTags will appear
   "style": "", // Bibliography display style, e.g., apa. Leave blank for default which is chicago-note-bibliography.
   "limit": 10, // Max number of results to retrieve per page
   "urlElementId": "searchUrl", // Element to display search URL
   "countElementId": "resultCount", // Element showing number of results
   "pagesTopElementId": "paginationTop", // Element to display result page links above results
   "pagesBotElementId": "paginationBot", // Element to display result page links below results
   "showPages": 5, // MUST BE ODD NUMBER! Max number of page links to show
   "sortDiv": "sortDiv" // Element with interactive sort options
};

// Get URL arguments
function getParameterByName(name, url) {
   if (!url) url = window.location.href;
   name = name.replace(/[\[\]]/g, "\\$&");
   var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
      results = regex.exec(url);
   if (!results) return null;
   if (!results[2]) return "";
   return decodeURIComponent(results[2].replace(/\+/g, " "));
}

// Parse Zotero search results into HTML
function parseZoteroResults(resultText) {
   function parseYear(text) {
      function isYear(yearText) {
         // Assumes year is between 1000 and current year + 5
         try {
            var yr = parseInt(yearText);
            var maxAllowable = (new Date().getFullYear()) + 5;
            return (String(yr) === yearText && yr >= 1000 && yr <= maxAllowable)
         } catch (err) {
            return false;
         }
      }

      if (text) {
         if (isYear(text.slice(0, 4))) {
            return text.slice(0, 4);
         } else if (isYear(text.slice(-4))) {
            return text.slice(-4);
         } else {
            return text;
         }
      } else {
         return "";
      }
   }

   function parseType(text) {
      switch (text) {
         case "artwork":
            return "Artwork";
         case "audioRecording":
            return "Audio Recording";
         case "bill":
            return "Bill";
         case "blogPost":
            return "Blog Post";
         case "book":
            return "Book";
         case "bookSection":
            return "Book Section";
         case "case":
            return "Case";
         case "computerProgram":
            return "Computer Program";
         case "conferencePaper":
            return "In Proceedings";
         case "dictionaryEntry":
            return "Dictionary Entry";
         case "document":
            return "Document";
         case "email":
            return "Email";
         case "encyclopediaArticle":
            return "Encyclopedia Article";
         case "film":
            return "Film";
         case "forumPost":
            return "Forum Post";
         case "hearing":
            return "Hearing";
         case "instantMessage":
            return "Instant Message";
         case "interview":
            return "Interview";
         case "journalArticle":
            return "Journal Article";
         case "letter":
            return "Letter";
         case "magazineArticle":
            return "Magazine Article";
         case "manuscript":
            return "Manuscript";
         case "map":
            return "Map";
         case "newspaperArticle":
            return "Newspaper Article";
         case "patent":
            return "Patent";
         case "podcast":
            return "Podcast";
         case "presentation":
            return "Presentation";
         case "radioBroadcast":
            return "Radio Broadcast";
         case "report":
            return "Report";
         case "statute":
            return "Statute";
         case "thesis":
            return "Thesis";
         case "tvBroadcast":
            return "TV Broadcast";
         case "videoRecording":
            return "Video Recording";
         case "webpage":
            return "Web Page";
         case "note":
            return "Note";
         default:
            return "Other"
      }
   }

   function parseShowTags(tags) {
      function objToString(tagObj) {
         return tagObj["tag"];
      }
      var tagArray = tags.map(objToString);
      var matches = tagArray.filter(function (n) {
         return ZOTERO_CONFIG["showTags"].indexOf(n) !== -1;
      });
      return matches.join(", ");
   }

   var results = JSON.parse(resultText);
   var sortDiv = document.getElementById(ZOTERO_CONFIG["sortDiv"]);
   if (sortDiv) {
      if (results.length)
         sortDiv.style.display = "block";
      else
         sortDiv.style.display = "none";
   }
   if (!results.length) {
      return "
Your search returned no results.
";
   }

   var header = '

';
   var showYear = (ZOTERO_CONFIG["includeCols"].indexOf("Year") !== -1);
   var showType = (ZOTERO_CONFIG["includeCols"].indexOf("Type") !== -1);
   var showTags = (ZOTERO_CONFIG["includeCols"].indexOf("ShowTags") !== -1);
   if (ZOTERO_CONFIG["includeCols"].length > 0) {
      header += "

";
      if (showYear) {
         header += "Year	";
      }
      header += "Citation	";
      if (showType) {
         header += "Type	";
      }
      if (showTags) {
         header += "" + ZOTERO_CONFIG["showTagColName"] + "	";
      }
      header += "

";
   }

   var rows = [header];

   for (var i = 0; i < results.length; i++) {
      var result = results[i];
      var year = parseYear(result["data"]["date"]);
      if (!year) {
         year = parseYear(result["data"]["issueDate"]);
      }
      var itemType = parseType(result["data"]["itemType"]);
      var tagsToShow = parseShowTags(result["data"]["tags"]);
      var row = "
";
      if (showYear) {
         row += "" + year + "	";
      }
      row += "" + result["bib"] + "	";
      if (showType) {
         row += "" + itemType + "	";
      }
      if (showTags) {
         row += "" + tagsToShow + "	";
      }
      row += "
"
      rows.push(row);
   }
   rows.push("

");
   return rows.join("");
}

function showLoading(isLoading) {
   var x = document.getElementById("loading-div");
   if (isLoading) {
      document.body.style.cursor = "wait";
      x.style.display = "block";
   } else {
      document.body.style.cursor = "default";
      x.style.display = "none";
   }
}

// Function to call if CORS request is successful
function successCallback(headers, response) {
   showLoading(false);

   // Write results to page
   var resultHtml = parseZoteroResults(response);
   var elementId = ZOTERO_CONFIG["resultsElementId"];
   document.getElementById(elementId).innerHTML = resultHtml;

   // Add links to additional search result pages if necessary
   var currentStart = getParameterByName("start");
   if (!currentStart) {
      currentStart = 0;
   } else {
      currentStart = parseInt(currentStart);
   }
   var count = parseInt(headers["total-results"]);
   var limit = parseInt(ZOTERO_CONFIG["limit"]);
   var showPages = parseInt(ZOTERO_CONFIG["showPages"]);
   var pageTopElementId = ZOTERO_CONFIG["pagesTopElementId"];
   var pageBotElementId = ZOTERO_CONFIG["pagesBotElementId"];
   showPageLinks(count, limit, showPages, currentStart, pageTopElementId);
   showPageLinks(count, limit, showPages, currentStart, pageBotElementId);
   var query = getParameterByName("q");
   showResultCount(query, count, limit, currentStart, ZOTERO_CONFIG["countElementId"]);
}

// Function to call if CORS request fails
function errorCallback() {
   showLoading(false);
   alert("There was an error making the request.");
}

// Writes CORS request URL to the page so user can see it
function showUrl(url) {
   var element = document.getElementById(ZOTERO_CONFIG["urlElementId"]);
   if (!element) return;
   var txt = '' + url + '';
   element.innerHTML = txt;
}

// Passes search URL and callbacks to CORS function
function searchZotero(query, itemType, sort, start) {
   var zotId = (ZOTERO_CONFIG["zotIdType"] === "group") ? "groups/" + ZOTERO_CONFIG["zotId"] : "users/" + ZOTERO_CONFIG["zotId"];
   var collection = (ZOTERO_CONFIG["collectionKey"] === "") ? "/" : "/collections/" + ZOTERO_CONFIG["collectionKey"] + "/";
   var base = "https://api.zotero.org/" + zotId + collection + "/items?v=3&include=bib,data";
   var style = (ZOTERO_CONFIG["style"] === "") ? "" : "&style=" + ZOTERO_CONFIG["style"];
   var params = "&q=" + encodeURI(query) + "&itemType=" + itemType +
      "&sort=" + sort + "&start=" + start + ZOTERO_CONFIG["filterTags"];
   var limit = "&limit=" + ZOTERO_CONFIG["limit"];
   var url = base + params + style + limit;
   showUrl(url);
   showLoading(true);
   makeCorsRequest(url, successCallback, errorCallback);
}

function initCollapsible(expanded) {
   // Handles collapsible sections
   function showHide(el, show) {
      if (show) el.style.maxHeight = "900px";
      else el.style.maxHeight = null;
   }

   // Expand if user tabs into hidden element
   function listenForFocus(collapsibleEl, toggle) {
      function addFocusListener(collapsibleEl, tagName, toggle) {
         var els = collapsibleEl.getElementsByTagName(tagName);
         var i;
         for (i = 0; i < els.length; i++) {
            els[i].onfocus = function () {
               if (!toggle.checked) toggle.click();
            };
         };
      }
      addFocusListener(collapsibleEl, "SELECT", toggle);
      addFocusListener(collapsibleEl, "INPUT", toggle);
   }

   // Collapse when checked
   var els = document.getElementsByClassName("collapse-toggle");
   var i;
   for (i = 0; i < els.length; i++) {
      var el = els[i];
      if (el.type && el.type === 'checkbox') {
         el.checked = expanded;
         var target = document.getElementById(el.getAttribute("aria-controls"));
         listenForFocus(target, el);
         showHide(target, expanded);
         el.setAttribute("aria-expanded", expanded.toString());
         el.onchange = function () {
            showHide(target, this.checked);
            this.setAttribute("aria-expanded", this.checked.toString());
         };
      }
   }
   // Toggle checkbox when user presses space or enter on label
   els = document.getElementsByClassName("lbl-toggle");
   for (i = 0; i < els.length; i++) {
      var label = els[i];
      label.onkeydown = function (e) {
         if (e.which === 32 || e.which === 13) {
            e.preventDefault();
            this.click();
         };
      };
   };
}

function initForm(formId, expanded) {
   initCollapsible(expanded);

   var sortControl = document.getElementById("visibleSort");
   if (sortControl) {
      sortControl.onchange = function () {
         var hiddenSortControl = document.getElementById("sort");
         hiddenSortControl.value = this.options[this.selectedIndex].value;
         var form = document.getElementById(formId);
         form.submit();
      };
   }
}

// Selects the desired value in the Select control. If value is not in the
// control, then first index is used. Returns actual selected value.  If
// control is not present, input value is returned.
function setSelectValue(elId, desiredValue) {
   var el = document.getElementById(elId);
   if (!el || !el.length) return desiredValue;
   var result = el[0].value;
   for (var i = 0; i < el.length; i++) {
      if (desiredValue === el[i].value) {
         el[i].selected = true;
         result = desiredValue;
         break;
      }
   }
   return result;
}

// When the window loads, read query parameters and perform search
window.onload = function () {
   var query = getParameterByName("q") || "";
   var itemTypeParam = getParameterByName("itemType") || "-attachment";
   var expanded = Boolean(getParameterByName("expanded"));
   var pageStart = getParameterByName("start") || 0;
   var sortParam = getParameterByName("sort") || "date";

   document.forms.zoteroSearchForm.q.value = query;
   var itemType = setSelectValue("itemType", itemTypeParam);
   var sort = setSelectValue("visibleSort", sortParam);
   var sortHiddenInput = document.getElementById("sort");
   if (sortHiddenInput) {
      document.forms.zoteroSearchForm.sort.value = sort;
   }

   initForm("zoteroSearchForm", expanded);

   searchZotero(query, itemType, sort, pageStart);
};<|endoftext|>


<commit_before>var ImmediateInterceptor = require("./detail/ImmediateInterceptor");
var TimerInterceptor = require("./detail/TimerInterceptor");
var ProcessTimerInterceptor = require("./detail/ProcessTimerInterceptor");
var DateInterceptor = require("./detail/DateInterceptor");
var TimeForwarder = require("./detail/TimeForwarder");
var TimeServer = require("./detail/TimeServer");
var APICreator = require("./detail/APICreator");
var Configuration = require("./detail/Configuration");

function enterRejectingState(actor) {
  actor.advanceTime = Promise.reject.bind(undefined, Error("Cannot advance time if timers are not intercepted!"));
  actor.blockSystem = Promise.reject.bind(undefined, Error("Cannot block system if timers are not intercepted!"));
  actor.expireAllTimeouts = Promise.reject.bind(undefined, Error("Cannot expire timeouts if timers are not intercepted!"));
  actor.forwardTimeToNextTimer = Promise.reject.bind(undefined, Error("Cannot forward time if timers are not intercepted!"));
}

function leaveRejectingState(actor) {
  actor.advanceTime = Zurvan.prototype.advanceTime;
  actor.blockSystem = Zurvan.prototype.blockSystem;
  actor.expireAllTimeouts = Zurvan.prototype.expireAllTimeouts;
  actor.forwardTimeToNextTimer = Zurvan.prototype.forwardTimeToNextTimer;
}

// me sad, but timeouts are global stuff :(
var areTimersIntercepted = false;

function Zurvan(config) {
  this.globalConfig = config;
  this.timeServer = new TimeServer();

  this.immediateInterceptor = new ImmediateInterceptor();
  this.timerInterceptor = new TimerInterceptor(this.timeServer);

  this.timeForwarder = new TimeForwarder(this.timeServer, this.timerInterceptor, this.immediateInterceptor);

  this.processTimerInterceptor = new ProcessTimerInterceptor(this.timeServer);
  this.dateInterceptor = new DateInterceptor(this.timeServer);

  enterRejectingState(this);
}

Zurvan.prototype.interceptTimers = function(config) {
  var that = this;
  return new Promise(function(resolve, reject) {
    if(areTimersIntercepted) {
	  return reject(Error("Cannot intercept timers that are already intercepted!"));
	}
	return resolve();
  }).then(function() {
    that.config = Configuration.merge(config, that.globalConfig);
    areTimersIntercepted = true;
	that.isActiveInterceptor = true;
	that.timeServer.setupTime(that.config.timeSinceStartup, that.config.systemTime);

    that.timerInterceptor.intercept(that.config);
    that.immediateInterceptor.intercept();
    that.dateInterceptor.intercept();

	if(!that.config.ignoreProcessTimers) {
      that.processTimerInterceptor.intercept();
	}

    leaveRejectingState(that);
	return that.waitForEmptyQueue();
  });
};

Zurvan.prototype.releaseTimers = function() {
  var that = this;
  return new Promise(function(resolve, reject) {
    if(!areTimersIntercepted) {
      return reject(Error("Cannot release timers that were not intercepted"));
	}

    return resolve();
  }).then(function() {
    return that.timeForwarder.stopForwarding();
  }).then(function() {
    return that.waitForEmptyQueue();
  }).then(function() {
  	if(!that.config.ignoreProcessTimers) {
      that.processTimerInterceptor.release();
	}

	that.dateInterceptor.release();
    that.immediateInterceptor.release();
	that.timerInterceptor.release();

    areTimersIntercepted = false;
    enterRejectingState(that);
  });
};

Zurvan.prototype.setSystemTime = function(newSystemTime) {
  return this.timeServer.setSystemTime(newSystemTime);
};

Zurvan.prototype.advanceTime = function(timeToForward) {
  return this.timeForwarder.advanceTime(timeToForward);
};

Zurvan.prototype.blockSystem = function(timeToBlock) {
  return this.timeForwarder.blockSystem(timeToBlock);
};

Zurvan.prototype.expireAllTimeouts = function() {
  return this.timeForwarder.expireAllTimeouts();
};

Zurvan.prototype.forwardTimeToNextTimer = function() {
  return this.timeForwarder.forwardTimeToNextTimer();
};

Zurvan.prototype.waitForEmptyQueue = function() {
  return this.advanceTime(0);
};

var defaultZurvanConfiguration = {
  timeSinceStartup: 0,
  systemTime: 0,
  acceptEvalTimers: false,
  denyImplicitTimer: false,
  denyTimersShorterThan1Ms: false,
  ignoreProcessTimers: false
};

var apiFunctions = ["releaseTimers", "interceptTimers", "setSystemTime", "advanceTime",
  "blockSystem", "expireAllTimeouts", "forwardTimeToNextTimer", "waitForEmptyQueue"];

function createZurvanAPI(newDefaultConfig) {
  var configuration = Configuration.merge(newDefaultConfig, defaultZurvanConfiguration);
  var api = APICreator.createAPI(new Zurvan(configuration), apiFunctions);

  api.withDefaultConfiguration = function(config) {
    return createZurvanAPI(Configuration.merge(config, configuration));
  };

  return api;
}

module.exports = createZurvanAPI();<commit_msg>OK, maybe it's a little better now<commit_after>var ImmediateInterceptor = require("./detail/ImmediateInterceptor");
var TimerInterceptor = require("./detail/TimerInterceptor");
var ProcessTimerInterceptor = require("./detail/ProcessTimerInterceptor");
var DateInterceptor = require("./detail/DateInterceptor");
var TimeForwarder = require("./detail/TimeForwarder");
var TimeServer = require("./detail/TimeServer");
var APICreator = require("./detail/APICreator");
var Configuration = require("./detail/Configuration");

function enterRejectingState(actor) {
  actor.advanceTime = Promise.reject.bind(undefined, Error("Cannot advance time if timers are not intercepted!"));
  actor.blockSystem = Promise.reject.bind(undefined, Error("Cannot block system if timers are not intercepted!"));
  actor.expireAllTimeouts = Promise.reject.bind(undefined, Error("Cannot expire timeouts if timers are not intercepted!"));
  actor.forwardTimeToNextTimer = Promise.reject.bind(undefined, Error("Cannot forward time if timers are not intercepted!"));
}

function enterForwardingState(actor) {
  actor.advanceTime = function(timeToForward) {
    return this.timeForwarder.advanceTime(timeToForward);
  };
  actor.blockSystem = function(timeToBlock) {
    return this.timeForwarder.blockSystem(timeToBlock);
  };
  actor.expireAllTimeouts = function() {
    return this.timeForwarder.expireAllTimeouts();
  };
  actor.forwardTimeToNextTimer = function() {
    return this.timeForwarder.forwardTimeToNextTimer();
  };
}

// me sad, but timeouts are global stuff :(
var areTimersIntercepted = false;

function Zurvan(config) {
  this.globalConfig = config;
  this.timeServer = new TimeServer();

  this.immediateInterceptor = new ImmediateInterceptor();
  this.timerInterceptor = new TimerInterceptor(this.timeServer);

  this.timeForwarder = new TimeForwarder(this.timeServer, this.timerInterceptor, this.immediateInterceptor);

  this.processTimerInterceptor = new ProcessTimerInterceptor(this.timeServer);
  this.dateInterceptor = new DateInterceptor(this.timeServer);

  enterRejectingState(this);
}

Zurvan.prototype.interceptTimers = function(config) {
  var that = this;
  return new Promise(function(resolve, reject) {
    if(areTimersIntercepted) {
	  return reject(Error("Cannot intercept timers that are already intercepted!"));
	}
	return resolve();
  }).then(function() {
    that.config = Configuration.merge(config, that.globalConfig);
    areTimersIntercepted = true;
	that.isActiveInterceptor = true;
	that.timeServer.setupTime(that.config.timeSinceStartup, that.config.systemTime);

    that.timerInterceptor.intercept(that.config);
    that.immediateInterceptor.intercept();
    that.dateInterceptor.intercept();

	if(!that.config.ignoreProcessTimers) {
      that.processTimerInterceptor.intercept();
	}

    enterForwardingState(that);
	return that.waitForEmptyQueue();
  });
};

Zurvan.prototype.releaseTimers = function() {
  var that = this;
  return new Promise(function(resolve, reject) {
    if(!areTimersIntercepted) {
      return reject(Error("Cannot release timers that were not intercepted"));
	}

    return resolve();
  }).then(function() {
    return that.timeForwarder.stopForwarding();
  }).then(function() {
    return that.waitForEmptyQueue();
  }).then(function() {
  	if(!that.config.ignoreProcessTimers) {
      that.processTimerInterceptor.release();
	}

	that.dateInterceptor.release();
    that.immediateInterceptor.release();
	that.timerInterceptor.release();

    areTimersIntercepted = false;
    enterRejectingState(that);
  });
};

Zurvan.prototype.setSystemTime = function(newSystemTime) {
  return this.timeServer.setSystemTime(newSystemTime);
};

Zurvan.prototype.waitForEmptyQueue = function() {
  return this.advanceTime(0);
};

var defaultZurvanConfiguration = {
  timeSinceStartup: 0,
  systemTime: 0,
  acceptEvalTimers: false,
  denyImplicitTimer: false,
  denyTimersShorterThan1Ms: false,
  ignoreProcessTimers: false
};

var apiFunctions = ["releaseTimers", "interceptTimers", "setSystemTime", "advanceTime",
  "blockSystem", "expireAllTimeouts", "forwardTimeToNextTimer", "waitForEmptyQueue"];

function createZurvanAPI(newDefaultConfig) {
  var configuration = Configuration.merge(newDefaultConfig, defaultZurvanConfiguration);
  var api = APICreator.createAPI(new Zurvan(configuration), apiFunctions);

  api.withDefaultConfiguration = function(config) {
    return createZurvanAPI(Configuration.merge(config, configuration));
  };

  return api;
}

module.exports = createZurvanAPI();<|endoftext|>


<commit_before>module.exports = {
  cmd: 'npm install && npm run test'
}
<commit_msg>slack notifications channel<commit_after>module.exports = {
  cmd: 'npm install && npm run test',
  notifications: {
    slack: {
      channel: '#bots-monocle'
    },
  }
}
<|endoftext|>


<commit_before>// Part 1
for(s=p=0,a=document.body.innerText.split`\n`;p<a.length-1&p>=0;s++)p+=++a[p]-1;s

// Part 2
for(s=p=0,a=document.body.innerText.split`\n`;p<a.length-1&p>=0;s++){q=p;p+=+a[p];a[q]-=a[q]>2?1:-1}s
<commit_msg>Shorter code by reversing position of ++<commit_after>// Part 1
for(s=p=0,a=document.body.innerText.split`\n`;p<a.length-1&p>=0;s++)p+=a[p]++;s

// Part 2
for(s=p=0,a=document.body.innerText.split`\n`;p<a.length-1&p>=0;s++){q=p;p+=+a[p];a[q]-=a[q]>2?1:-1}s
<|endoftext|>


<commit_before>import { mochaInstance } from 'meteor/practicalmeteor:mocha-core';
import { startBrowser } from 'meteor/meteortesting:browser-tests';

import setArgs from './runtimeArgs';

const { mochaOptions, runnerOptions } = setArgs();
const { grep, invert, reporter, serverReporter, xUnitOutput } = mochaOptions || {};

// Since intermingling client and server log lines would be confusing,
// the idea here is to buffer all client logs until server tests have
// finished running and then dump the buffer to the screen and continue
// logging in real time after that if client tests are still running.

let serverTestsDone = false;
const clientLines = [];
function clientLogBuffer(line) {
  if (serverTestsDone) {
    // printing and removing the extra new-line character. The first was added by the client log, the second here.
    console.log(line.replace(/\n$/, ''));
  } else {
    clientLines.push(line);
  }
}

function printHeader(type) {
  const lines = [
    '\n--------------------------------',
    `----- RUNNING ${type} TESTS -----`,
    '--------------------------------\n',
  ];
  lines.forEach((line) => {
    if (type === 'CLIENT') {
      clientLogBuffer(line);
    } else {
      console.log(line);
    }
  });
}

let callCount = 0;
let clientFailures = 0;
let serverFailures = 0;
function exitIfDone(type, failures) {
  callCount++;
  if (type === 'client') {
    clientFailures = failures;
  } else {
    serverFailures = failures;
    serverTestsDone = true;
    clientLines.forEach((line) => {
      // printing and removing the extra new-line character. The first was added by the client log, the second here.
      console.log(line.replace(/\n$/, ''));
    });
  }

  if (callCount === 2) {
    // We only need to show this final summary if we ran both kinds of tests in the same console
    if (runnerOptions.runServer && runnerOptions.runClient && runnerOptions.browserDriver) {
      console.log('All tests finished!\n');
      console.log('--------------------------------');
      console.log(`SERVER FAILURES: ${serverFailures}`);
      console.log(`CLIENT FAILURES: ${clientFailures}`);
      console.log('--------------------------------');
    }

    // if no env for TEST_WATCH, tests should exit when done
    if (!runnerOptions.testWatch) {
      if (clientFailures + serverFailures > 0) {
        process.exit(1); // exit with non-zero status if there were failures
      } else {
        process.exit(0);
      }
    }
  }
}

function serverTests(cb) {
  if (!runnerOptions.runServer) {
    console.log('SKIPPING SERVER TESTS BECAUSE TEST_SERVER=0');
    exitIfDone('server', 0);
    if (cb) cb();
    return;
  }

  printHeader('SERVER');

  if (grep) mochaInstance.grep(grep);
  if (invert) mochaInstance.options.invert = invert;
  mochaInstance.options.useColors = true;

  // We need to set the reporter when the tests actually run to ensure no conflicts with
  // other test driver packages that may be added to the app but are not actually being
  // used on this run.
  mochaInstance.reporter(serverReporter || reporter, {
    output: xUnitOutput,
  });

  mochaInstance.run((failureCount) => {
    exitIfDone('server', failureCount);
    if (cb) cb();
  });
}

function clientTests() {
  if (!runnerOptions.runClient) {
    console.log('SKIPPING CLIENT TESTS BECAUSE TEST_CLIENT=0');
    exitIfDone('client', 0);
    return;
  }

  if (!runnerOptions.browserDriver) {
    console.log('Load the app in a browser to run client tests, or set the TEST_BROWSER_DRIVER environment variable. ' +
      'See https://github.com/meteortesting/meteor-mocha/blob/master/README.md#run-app-tests');
    exitIfDone('client', 0);
    return;
  }

  printHeader('CLIENT');

  startBrowser({
    stdout(data) {
      clientLogBuffer(data.toString());
    },
    stderr(data) {
      clientLogBuffer(data.toString());
    },
    done(failureCount) {
      exitIfDone('client', failureCount);
    },
  });
}

// Before Meteor calls the `start` function, app tests will be parsed and loaded by Mocha
function start() {
  // Run in PARALLEL or SERIES
  // Running in series is a better default since it avoids db and state conflicts for newbs.
  // If you want parallel you will know these risks.
  if (runnerOptions.runParallel) {
    console.log('Warning: Running in parallel can cause side-effects from state/db sharing');

    serverTests();
    clientTests();
  } else {
    serverTests(() => {
      clientTests();
    });
  }
}

export { start };
<commit_msg>Log whether we are running app tests.<commit_after>import { mochaInstance } from 'meteor/practicalmeteor:mocha-core';
import { startBrowser } from 'meteor/meteortesting:browser-tests';

import setArgs from './runtimeArgs';

const { mochaOptions, runnerOptions } = setArgs();
const { grep, invert, reporter, serverReporter, xUnitOutput } = mochaOptions || {};

// Since intermingling client and server log lines would be confusing,
// the idea here is to buffer all client logs until server tests have
// finished running and then dump the buffer to the screen and continue
// logging in real time after that if client tests are still running.

let serverTestsDone = false;
const clientLines = [];
function clientLogBuffer(line) {
  if (serverTestsDone) {
    // printing and removing the extra new-line character. The first was added by the client log, the second here.
    console.log(line.replace(/\n$/, ''));
  } else {
    clientLines.push(line);
  }
}

function printHeader(type) {
  const lines = [
    '\n--------------------------------',
    Meteor.isAppTest ? `--- RUNNING APP ${type} TESTS ---` : `----- RUNNING ${type} TESTS -----`,
    '--------------------------------\n',
  ];
  lines.forEach((line) => {
    if (type === 'CLIENT') {
      clientLogBuffer(line);
    } else {
      console.log(line);
    }
  });
}

let callCount = 0;
let clientFailures = 0;
let serverFailures = 0;
function exitIfDone(type, failures) {
  callCount++;
  if (type === 'client') {
    clientFailures = failures;
  } else {
    serverFailures = failures;
    serverTestsDone = true;
    clientLines.forEach((line) => {
      // printing and removing the extra new-line character. The first was added by the client log, the second here.
      console.log(line.replace(/\n$/, ''));
    });
  }

  if (callCount === 2) {
    // We only need to show this final summary if we ran both kinds of tests in the same console
    if (runnerOptions.runServer && runnerOptions.runClient && runnerOptions.browserDriver) {
      console.log('All tests finished!\n');
      console.log('--------------------------------');
      console.log(`${Meteor.isAppTest ? 'APP ' : ''}SERVER FAILURES: ${serverFailures}`);
      console.log(`${Meteor.isAppTest ? 'APP ' : ''}CLIENT FAILURES: ${clientFailures}`);
      console.log('--------------------------------');
    }

    // if no env for TEST_WATCH, tests should exit when done
    if (!runnerOptions.testWatch) {
      if (clientFailures + serverFailures > 0) {
        process.exit(1); // exit with non-zero status if there were failures
      } else {
        process.exit(0);
      }
    }
  }
}

function serverTests(cb) {
  if (!runnerOptions.runServer) {
    console.log('SKIPPING SERVER TESTS BECAUSE TEST_SERVER=0');
    exitIfDone('server', 0);
    if (cb) cb();
    return;
  }

  printHeader('SERVER');

  if (grep) mochaInstance.grep(grep);
  if (invert) mochaInstance.options.invert = invert;
  mochaInstance.options.useColors = true;

  // We need to set the reporter when the tests actually run to ensure no conflicts with
  // other test driver packages that may be added to the app but are not actually being
  // used on this run.
  mochaInstance.reporter(serverReporter || reporter, {
    output: xUnitOutput,
  });

  mochaInstance.run((failureCount) => {
    exitIfDone('server', failureCount);
    if (cb) cb();
  });
}

function clientTests() {
  if (!runnerOptions.runClient) {
    console.log('SKIPPING CLIENT TESTS BECAUSE TEST_CLIENT=0');
    exitIfDone('client', 0);
    return;
  }

  if (!runnerOptions.browserDriver) {
    console.log('Load the app in a browser to run client tests, or set the TEST_BROWSER_DRIVER environment variable. ' +
      'See https://github.com/meteortesting/meteor-mocha/blob/master/README.md#run-app-tests');
    exitIfDone('client', 0);
    return;
  }

  printHeader('CLIENT');

  startBrowser({
    stdout(data) {
      clientLogBuffer(data.toString());
    },
    stderr(data) {
      clientLogBuffer(data.toString());
    },
    done(failureCount) {
      exitIfDone('client', failureCount);
    },
  });
}

// Before Meteor calls the `start` function, app tests will be parsed and loaded by Mocha
function start() {
  // Run in PARALLEL or SERIES
  // Running in series is a better default since it avoids db and state conflicts for newbs.
  // If you want parallel you will know these risks.
  if (runnerOptions.runParallel) {
    console.log('Warning: Running in parallel can cause side-effects from state/db sharing');

    serverTests();
    clientTests();
  } else {
    serverTests(() => {
      clientTests();
    });
  }
}

export { start };
<|endoftext|>


<commit_before>// This file is auto gererated by build/build-entry.js

import VeCheckbox from './ve-checkbox';
import VeCheckboxGroup from './ve-checkbox-group';
import VeContextmenu from './ve-contextmenu';
import VeDropdown from './ve-dropdown';
import VeIcon from './ve-icon';
import VeLoading from './ve-loading';
import VeLocale from './ve-locale';
import VePagination from './ve-pagination';
import VeRadio from './ve-radio';
import VeSelect from './ve-select';
import VeTable from './ve-table';

const version = '2.12.3';
const components = [
  VeCheckbox,
  VeCheckboxGroup,
  VeContextmenu,
  VeDropdown,
  VeIcon,
  VeLoading,
  VeLocale,
  VePagination,
  VeRadio,
  VeSelect,
  VeTable
];

const install = Vue => {
  components.forEach(Component => {
    Vue.use(Component);
  });

  Vue.prototype.$veLoading = VeLoading;
  Vue.prototype.$veLocale = VeLocale;
};

/* istanbul ignore if */
if (typeof window !== 'undefined' && window.Vue) {
  install(window.Vue);
}

export {
  install,
  version,
  VeCheckbox,
  VeCheckboxGroup,
  VeContextmenu,
  VeDropdown,
  VeIcon,
  VeLoading,
  VeLocale,
  VePagination,
  VeRadio,
  VeSelect,
  VeTable
};

export default {
  install,
  version,
  VeCheckbox,
  VeCheckboxGroup,
  VeContextmenu,
  VeDropdown,
  VeIcon,
  VeLoading,
  VeLocale,
  VePagination,
  VeRadio,
  VeSelect,
  VeTable
};
<commit_msg>v2.13.0<commit_after>// This file is auto gererated by build/build-entry.js

import VeCheckbox from './ve-checkbox';
import VeCheckboxGroup from './ve-checkbox-group';
import VeContextmenu from './ve-contextmenu';
import VeDropdown from './ve-dropdown';
import VeIcon from './ve-icon';
import VeLoading from './ve-loading';
import VeLocale from './ve-locale';
import VePagination from './ve-pagination';
import VeRadio from './ve-radio';
import VeSelect from './ve-select';
import VeTable from './ve-table';

const version = '2.13.0';
const components = [
  VeCheckbox,
  VeCheckboxGroup,
  VeContextmenu,
  VeDropdown,
  VeIcon,
  VeLoading,
  VeLocale,
  VePagination,
  VeRadio,
  VeSelect,
  VeTable
];

const install = Vue => {
  components.forEach(Component => {
    Vue.use(Component);
  });

  Vue.prototype.$veLoading = VeLoading;
  Vue.prototype.$veLocale = VeLocale;
};

/* istanbul ignore if */
if (typeof window !== 'undefined' && window.Vue) {
  install(window.Vue);
}

export {
  install,
  version,
  VeCheckbox,
  VeCheckboxGroup,
  VeContextmenu,
  VeDropdown,
  VeIcon,
  VeLoading,
  VeLocale,
  VePagination,
  VeRadio,
  VeSelect,
  VeTable
};

export default {
  install,
  version,
  VeCheckbox,
  VeCheckboxGroup,
  VeContextmenu,
  VeDropdown,
  VeIcon,
  VeLoading,
  VeLocale,
  VePagination,
  VeRadio,
  VeSelect,
  VeTable
};
<|endoftext|>


<commit_before>import gql from 'graphql-tag';
import { t, ngettext, msgid } from 'ttag';
import Router, { useRouter } from 'next/router';
import url from 'url';
import { useQuery } from '@apollo/react-hooks';

import ButtonGroup from '@material-ui/core/ButtonGroup';
import Button from '@material-ui/core/Button';
import TextField from '@material-ui/core/TextField';
import MenuItem from '@material-ui/core/MenuItem';
import InputAdornment from '@material-ui/core/InputAdornment';
import SortIcon from '@material-ui/icons/Sort';
import Grid from '@material-ui/core/Grid';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import Checkbox from '@material-ui/core/Checkbox';
import Typography from '@material-ui/core/Typography';

import withData from 'lib/apollo';
import AppLayout from 'components/AppLayout';
import ArticleItem from 'components/ArticleItem';
import Pagination from 'components/Pagination';
import SearchInput from 'components/SearchInput';

const DEFAULT_ORDER_BY = 'lastRequestedAt';
const DEFAULT_TYPE_FILTER = 'unsolved';
const DEFAULT_REPLY_REQUEST_COUNT = 2;

const LIST_ARTICLES = gql`
  query ListArticles(
    $filter: ListArticleFilter
    $orderBy: [ListArticleOrderBy]
    $before: String
    $after: String
  ) {
    ListArticles(
      filter: $filter
      orderBy: $orderBy
      before: $before
      after: $after
      first: 25
    ) {
      edges {
        node {
          ...ArticleItem
        }
        cursor
      }
    }
  }
  ${ArticleItem.fragments.ArticleItem}
`;

const LIST_STAT = gql`
  query ListArticlesStat(
    $filter: ListArticleFilter
    $orderBy: [ListArticleOrderBy]
  ) {
    ListArticles(filter: $filter, orderBy: $orderBy, first: 25) {
      pageInfo {
        firstCursor
        lastCursor
      }
      totalCount
    }
  }
`;

/**
 * @param {object} urlQuery - URL query object
 * @returns {object} ListArticleFilter
 */
function urlQuery2Filter({
  filter = DEFAULT_TYPE_FILTER,
  q,
  replyRequestCount = DEFAULT_REPLY_REQUEST_COUNT,
  searchUserByArticleId,
} = {}) {
  const filterObj = {};
  if (q) {
    filterObj.moreLikeThis = { like: q, minimumShouldMatch: '0' };
  }

  filterObj.replyRequestCount = { GT: replyRequestCount - 1 };

  if (filter === 'solved') {
    filterObj.replyCount = { GT: 0 };
  } else if (filter === 'unsolved') {
    filterObj.replyCount = { EQ: 0 };
  }

  if (searchUserByArticleId) {
    filterObj.fromUserOfArticleId = searchUserByArticleId;
  }

  // Return filterObj only when it is populated.
  if (!Object.keys(filterObj).length) {
    return undefined;
  }
  return filterObj;
}

/**
 * @param {object} urlQuery - URL query object
 * @returns {object[]} ListArticleOrderBy array
 */
function urlQuery2OrderBy({ q, orderBy = DEFAULT_ORDER_BY } = {}) {
  // If there is query text, sort by _score first

  if (q) {
    return [{ _score: 'DESC' }, { [orderBy]: 'DESC' }];
  }

  return [{ [orderBy]: 'DESC' }];
}

/**
 * @param {object} urlQuery
 */
function goToUrlQueryAndResetPagination(urlQuery) {
  delete urlQuery.before;
  delete urlQuery.after;
  Router.push(`${location.pathname}${url.format({ query: urlQuery })}`);
}

function ArticleFilter({ filter = DEFAULT_TYPE_FILTER, onChange = () => {} }) {
  return (
    <ButtonGroup size="small" variant="outlined">
      <Button
        disabled={filter === 'unsolved'}
        onClick={() => onChange('unsolved')}
      >
        {t`Not replied`}
      </Button>
      <Button disabled={filter === 'solved'} onClick={() => onChange('solved')}>
        {t`Replied`}
      </Button>
      <Button disabled={filter === 'all'} onClick={() => onChange('all')}>
        {t`All`}
      </Button>
    </ButtonGroup>
  );
}

function SortInput({ orderBy = DEFAULT_ORDER_BY, onChange = () => {} }) {
  return (
    <TextField
      label={t`Sort by`}
      select
      InputProps={{
        startAdornment: (
          <InputAdornment position="start">
            <SortIcon />
          </InputAdornment>
        ),
      }}
      value={orderBy}
      onChange={e => onChange(e.target.value)}
    >
      <MenuItem value="lastRequestedAt">{t`Most recently asked`}</MenuItem>
      <MenuItem value="replyRequestCount">{t`Most asked`}</MenuItem>
    </TextField>
  );
}

function ArticleListPage() {
  const { query } = useRouter();

  const listQueryVars = {
    filter: urlQuery2Filter(query),
    orderBy: urlQuery2OrderBy(query),
  };

  const {
    loading,
    data: { ListArticles: articleData },
  } = useQuery(LIST_ARTICLES, {
    variables: {
      ...listQueryVars,
      before: query.before,
      after: query.after,
    },
  });

  // Separate these stats query so that it will be cached by apollo-client and sends no network request
  // on page change, but still works when filter options are updated.
  //
  const {
    loading: statsLoading,
    data: { ListArticles: statsData },
  } = useQuery(LIST_STAT, {
    variables: listQueryVars,
  });

  const showOneTimeMessages = +query.replyRequestCount === 1;

  return (
    <AppLayout>
      <Grid container spacing={2}>
        <Grid item>
          <ArticleFilter
            filter={query.filter}
            onChange={filter =>
              goToUrlQueryAndResetPagination({ ...query, filter })
            }
          />
        </Grid>
        <Grid item>
          <SearchInput
            q={query.q}
            onChange={q => goToUrlQueryAndResetPagination({ ...query, q })}
          />
        </Grid>
      </Grid>
      <FormControlLabel
        control={
          <Checkbox
            checked={showOneTimeMessages}
            onChange={e =>
              goToUrlQueryAndResetPagination({
                ...query,
                replyRequestCount: e.target.checked ? 1 : 2,
              })
            }
          />
        }
        label={t`Include messages reported only 1 time`}
      />

        {query.q ? (
          t`Sort by Relevance`
        ) : (
          <SortInput
            orderBy={query.orderBy}
            onChange={orderBy =>
              goToUrlQueryAndResetPagination({ ...query, orderBy })
            }
          />
        )}

        {statsLoading
          ? 'Loading...'
          : ngettext(
              msgid`${statsData.totalCount} collected message`,
              `${statsData.totalCount} collected messages`,
              statsData.totalCount
            )}

      {loading ? (
        'Loading....'
      ) : (
        <>
          <Pagination
            query={query}
            pageInfo={statsData?.pageInfo}
            edges={articleData?.edges}
          />

            {articleData.edges.map(({ node }) => {
              return <ArticleItem key={node.id} article={node} />;
            })}

          <Pagination
            query={query}
            pageInfo={statsData?.pageInfo}
            edges={articleData?.edges}
          />
          {!showOneTimeMessages && (
            <Typography variant="body2" component="p">
              {t`Shows messages reported multiple times by default.`}{' '}

                  goToUrlQueryAndResetPagination({
                    ...query,
                    replyRequestCount: 1,
                  })
                }
              >
                {t`Click here to include messages only reported 1 time.`}

            </Typography>
          )}
        </>
      )}

    </AppLayout>
  );
}

export default withData(ArticleListPage);
<commit_msg>[articles] add title<commit_after>import gql from 'graphql-tag';
import { t, ngettext, msgid } from 'ttag';
import Router, { useRouter } from 'next/router';
import Head from 'next/head';
import url from 'url';
import { useQuery } from '@apollo/react-hooks';

import ButtonGroup from '@material-ui/core/ButtonGroup';
import Button from '@material-ui/core/Button';
import TextField from '@material-ui/core/TextField';
import MenuItem from '@material-ui/core/MenuItem';
import InputAdornment from '@material-ui/core/InputAdornment';
import SortIcon from '@material-ui/icons/Sort';
import Grid from '@material-ui/core/Grid';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import Checkbox from '@material-ui/core/Checkbox';
import Typography from '@material-ui/core/Typography';

import withData from 'lib/apollo';
import AppLayout from 'components/AppLayout';
import ArticleItem from 'components/ArticleItem';
import Pagination from 'components/Pagination';
import SearchInput from 'components/SearchInput';

const DEFAULT_ORDER_BY = 'lastRequestedAt';
const DEFAULT_TYPE_FILTER = 'unsolved';
const DEFAULT_REPLY_REQUEST_COUNT = 2;

const LIST_ARTICLES = gql`
  query ListArticles(
    $filter: ListArticleFilter
    $orderBy: [ListArticleOrderBy]
    $before: String
    $after: String
  ) {
    ListArticles(
      filter: $filter
      orderBy: $orderBy
      before: $before
      after: $after
      first: 25
    ) {
      edges {
        node {
          ...ArticleItem
        }
        cursor
      }
    }
  }
  ${ArticleItem.fragments.ArticleItem}
`;

const LIST_STAT = gql`
  query ListArticlesStat(
    $filter: ListArticleFilter
    $orderBy: [ListArticleOrderBy]
  ) {
    ListArticles(filter: $filter, orderBy: $orderBy, first: 25) {
      pageInfo {
        firstCursor
        lastCursor
      }
      totalCount
    }
  }
`;

/**
 * @param {object} urlQuery - URL query object
 * @returns {object} ListArticleFilter
 */
function urlQuery2Filter({
  filter = DEFAULT_TYPE_FILTER,
  q,
  replyRequestCount = DEFAULT_REPLY_REQUEST_COUNT,
  searchUserByArticleId,
} = {}) {
  const filterObj = {};
  if (q) {
    filterObj.moreLikeThis = { like: q, minimumShouldMatch: '0' };
  }

  filterObj.replyRequestCount = { GT: replyRequestCount - 1 };

  if (filter === 'solved') {
    filterObj.replyCount = { GT: 0 };
  } else if (filter === 'unsolved') {
    filterObj.replyCount = { EQ: 0 };
  }

  if (searchUserByArticleId) {
    filterObj.fromUserOfArticleId = searchUserByArticleId;
  }

  // Return filterObj only when it is populated.
  if (!Object.keys(filterObj).length) {
    return undefined;
  }
  return filterObj;
}

/**
 * @param {object} urlQuery - URL query object
 * @returns {object[]} ListArticleOrderBy array
 */
function urlQuery2OrderBy({ q, orderBy = DEFAULT_ORDER_BY } = {}) {
  // If there is query text, sort by _score first

  if (q) {
    return [{ _score: 'DESC' }, { [orderBy]: 'DESC' }];
  }

  return [{ [orderBy]: 'DESC' }];
}

/**
 * @param {object} urlQuery
 */
function goToUrlQueryAndResetPagination(urlQuery) {
  delete urlQuery.before;
  delete urlQuery.after;
  Router.push(`${location.pathname}${url.format({ query: urlQuery })}`);
}

function ArticleFilter({ filter = DEFAULT_TYPE_FILTER, onChange = () => {} }) {
  return (
    <ButtonGroup size="small" variant="outlined">
      <Button
        disabled={filter === 'unsolved'}
        onClick={() => onChange('unsolved')}
      >
        {t`Not replied`}
      </Button>
      <Button disabled={filter === 'solved'} onClick={() => onChange('solved')}>
        {t`Replied`}
      </Button>
      <Button disabled={filter === 'all'} onClick={() => onChange('all')}>
        {t`All`}
      </Button>
    </ButtonGroup>
  );
}

function SortInput({ orderBy = DEFAULT_ORDER_BY, onChange = () => {} }) {
  return (
    <TextField
      label={t`Sort by`}
      select
      InputProps={{
        startAdornment: (
          <InputAdornment position="start">
            <SortIcon />
          </InputAdornment>
        ),
      }}
      value={orderBy}
      onChange={e => onChange(e.target.value)}
    >
      <MenuItem value="lastRequestedAt">{t`Most recently asked`}</MenuItem>
      <MenuItem value="replyRequestCount">{t`Most asked`}</MenuItem>
    </TextField>
  );
}

function ArticleListPage() {
  const { query } = useRouter();

  const listQueryVars = {
    filter: urlQuery2Filter(query),
    orderBy: urlQuery2OrderBy(query),
  };

  const {
    loading,
    data: { ListArticles: articleData },
  } = useQuery(LIST_ARTICLES, {
    variables: {
      ...listQueryVars,
      before: query.before,
      after: query.after,
    },
  });

  // Separate these stats query so that it will be cached by apollo-client and sends no network request
  // on page change, but still works when filter options are updated.
  //
  const {
    loading: statsLoading,
    data: { ListArticles: statsData },
  } = useQuery(LIST_STAT, {
    variables: listQueryVars,
  });

  const showOneTimeMessages = +query.replyRequestCount === 1;

  return (
    <AppLayout>

        <title>{t`Article list`}</title>

      <Grid container spacing={2}>
        <Grid item>
          <ArticleFilter
            filter={query.filter}
            onChange={filter =>
              goToUrlQueryAndResetPagination({ ...query, filter })
            }
          />
        </Grid>
        <Grid item>
          <SearchInput
            q={query.q}
            onChange={q => goToUrlQueryAndResetPagination({ ...query, q })}
          />
        </Grid>
      </Grid>
      <FormControlLabel
        control={
          <Checkbox
            checked={showOneTimeMessages}
            onChange={e =>
              goToUrlQueryAndResetPagination({
                ...query,
                replyRequestCount: e.target.checked ? 1 : 2,
              })
            }
          />
        }
        label={t`Include messages reported only 1 time`}
      />

        {query.q ? (
          t`Sort by Relevance`
        ) : (
          <SortInput
            orderBy={query.orderBy}
            onChange={orderBy =>
              goToUrlQueryAndResetPagination({ ...query, orderBy })
            }
          />
        )}

        {statsLoading
          ? 'Loading...'
          : ngettext(
              msgid`${statsData.totalCount} collected message`,
              `${statsData.totalCount} collected messages`,
              statsData.totalCount
            )}

      {loading ? (
        'Loading....'
      ) : (
        <>
          <Pagination
            query={query}
            pageInfo={statsData?.pageInfo}
            edges={articleData?.edges}
          />

            {articleData.edges.map(({ node }) => {
              return <ArticleItem key={node.id} article={node} />;
            })}

          <Pagination
            query={query}
            pageInfo={statsData?.pageInfo}
            edges={articleData?.edges}
          />
          {!showOneTimeMessages && (
            <Typography variant="body2" component="p">
              {t`Shows messages reported multiple times by default.`}{' '}

                  goToUrlQueryAndResetPagination({
                    ...query,
                    replyRequestCount: 1,
                  })
                }
              >
                {t`Click here to include messages only reported 1 time.`}

            </Typography>
          )}
        </>
      )}

    </AppLayout>
  );
}

export default withData(ArticleListPage);
<|endoftext|>


<commit_before>const html = require('choo/html')
const sf = require('sheetify')
const _ = require('lodash')
const tutorials = require('../assets/tutorial')
const buttonView = require('../elements/button')

const prefix = sf`
  :host {
    color: #404040;
    height: 100%;
    width: 100%;
    overflow-y: scroll;
  }

  :host > div {
    margin-bottom: 25px;
  }

  :host > .logo {
    width: 100%;
    height: 35%;
    background-image: url('assets/icons/robotopia.svg');
    background-size: contain;
    background-position: center;
    background-repeat: no-repeat;
    margin-bottom: 0;
  }

  :host h2 {
    color: #03a9f4;
    text-align: center;
  }

  :host .editor {
    text-align: center;
  }

  :host ol {
    padding-top: 0;
    margin-top: 0;
  }

  li {
    font-size: 1.5em;
    padding: 3px;
  }

  :host a {
    color: #404040;
  }

  :host a:hover {
    color: #dddddd;
  }

  :host > .credits {
    margin-top: 50px;
    text-decoration: underline;
  }

  :host .category {
    width: 95%;
    border-bottom: 1px solid black;
    margin: 20px auto;
  }

  :host .name {
    text-align: left;
    font-size: 150%;
    font-weight: 600;
  }

  :host .level {
    width: 256px;
    height: 256px;
    overflow: hidden;
    display: inline-block;
    margin: 20px;
    position: relative;
  }

  :host .level > div {
    width: 100%;
    height: 100%;
    background-size: contain;
    background-position: center;
    background-repeat: no-repeat;
    position: absolute;
  }
`

const overviewView = (state, prev, send) => {
  const editorButton = buttonView({
    label: 'Load Editor',
    onClick: () => send('location:set', '#editor')
  })

  return html`

Tutorials:

        ${getAllTutorials(tutorials)}

Check out the Editor:

        ${editorButton}

      © Credits

  `
}

function getAllTutorials (tutorials) {
  return html`

    ${_.map(tutorials, getTutorialCategory)}

`

  function getTutorialCategory(category) {
    const name = category.categoryName
    return html`

${name}

        ${_.map(category.levels, getTutorial)}

`

    function getTutorial(level, index) {
      return html`

    `
    }

  }

}

module.exports = overviewView
<commit_msg>styling. made things fancier<commit_after>const html = require('choo/html')
const sf = require('sheetify')
const _ = require('lodash')
const tutorials = require('../assets/tutorial')
const buttonView = require('../elements/button')

const prefix = sf`
  :host {
    color: #404040;
    height: 100%;
    width: 100%;
    overflow-y: scroll;
  }

  :host > div {
    margin-bottom: 25px;
  }

  :host > .logo {
    width: 100%;
    height: 35%;
    background-image: url('assets/icons/robotopia.svg');
    background-size: contain;
    background-position: center;
    background-repeat: no-repeat;
    margin-bottom: 0;
  }

  :host h2 {
    color: #03a9f4;
    text-align: center;
  }

  :host .editor {
    text-align: center;
  }

  :host ol {
    padding-top: 0;
    margin-top: 0;
  }

  li {
    font-size: 1.5em;
    padding: 3px;
  }

  :host a {
    color: #404040;
  }

  :host a:hover {
    color: #dddddd;
  }

  :host > .credits {
    margin-top: 50px;
    text-decoration: underline;
  }

  :host .category {
    width: 95%;
    border-bottom: 1px solid black;
    margin: 20px auto;
    padding: 1em;
  }

  :host .name {
    text-align: left;
    font-size: 150%;
    font-weight: 600;
  }

  :host .level {
    width: 250px;
    overflow: hidden;
    display: inline-block;
    position: relative;
    margin: 20px;
    text-decoration: none;
  }

  :host .level:hover {
    opacity: 0.8;
    color: #404040;
  }

  :host .level > div{
    width: 100%;
    position: relative;
    text-align: center;
  }

  :host .levelImage {
    width: 250px;
    height: 250px;
    background-size: contain;
    background-position: center;
    background-repeat: no-repeat;
    margin-bottom: 5px;
  }

  :host .levelName {
    font-weight: 600;
    line-height: 150%;
  }
`

const overviewView = (state, prev, send) => {
  const editorButton = buttonView({
    label: 'Load Editor',
    onClick: () => send('location:set', '#editor')
  })

  return html`

Tutorials:

        ${getAllTutorials(tutorials)}

Check out the Editor:

        ${editorButton}

      © Credits

  `
}

function getAllTutorials (tutorials) {
  return html`

    ${_.map(tutorials, getTutorialCategory)}

`

  function getTutorialCategory(category) {
    const categoryName = category.categoryName
    return html`

${categoryName}

        ${_.map(category.levels, getTutorial)}

`

    function getTutorial(level, index) {
      const oneIndex = index + 1
      return html`

${level().label}

    `
    }

  }

}

module.exports = overviewView
<|endoftext|>


<commit_before>'use strict';

var AwesomeWorker = require('../index');

var plugin = function(cb) {
  var get = require('simple-get');
  var awesome = 'sindresorhus/awesome';
  var AWESOME_URL = 'https://raw.githubusercontent.com/' + awesome + '/master/readme.md';
  var REGEX = /(\*|-) \[(.*?)\]\((.*?)\)/g;

  get.concat(AWESOME_URL, (err, data, res) => {
    if (err && res.statusCode !== 200) {
      console.log(new Error('Unable to get the response.'));
    }

    let rawBody = data.toString().replace(/\n/g, '');
    let awesomeJson = {};

    let parseContent = (resolve, reject) => {
      let length = rawBody.split('##').length;
      let cnt = 0;
      rawBody.split('##').forEach((e) => {
        let category = e.split('- ')[0].trim();
        let match;
        if (!category.startsWith('#')) {
          awesomeJson[category] = [];
        }

        while ((match = REGEX.exec(e))) {
          if (!match[3].startsWith('#')) {
            awesomeJson[category].push({
              name: match[2],
              url:  match[3],
              description: match[4],
              cate: category,
            });

          }
        }

        cnt++;
        if (cnt === length) {
          // Finish parsing
          resolve();
        }
      });
    };

    let p = new Promise(parseContent);

    p.then(() => {
      cb(awesomeJson);
    });
  });
};

var w = new AwesomeWorker('awesome');
plugin((json) => {
  w.save(json);
});
<commit_msg>finetune<commit_after>'use strict';

var AwesomeWorker = require('../index');

var plugin = function(cb) {
  var get = require('simple-get');
  var awesome = 'sindresorhus/awesome';
  var AWESOME_URL = `https://raw.githubusercontent.com/${awesome}/master/readme.md`;
  var finalJson;

  get.concat(AWESOME_URL, (err, data, res) => {
    if (err && res.statusCode !== 200) {
      console.log(new Error('Unable to get the response.'));
    }

    cb(parseContent(data.toString().replace(/\n/g, '')));
  });
};

function parseContent(rawBody) {
  var length = rawBody.split('##').length;
  var cateArr = rawBody.split('##');
  var cnt = 0;
  var match = [];
  var awesomeJson = {};
  var REGEX = /(\*|-) \[(.*?)\]\((.*?)\)/g;

  for (let e of cateArr) {
    let category = e.split('- ')[0].trim();
    if (!category.startsWith('#')) {
      awesomeJson[category] = [];
    }

    while ((match = REGEX.exec(e))) {
      if (!match[3].startsWith('#')) {
        let obj = {
          name: match[2],
          url:  match[3],
          description: match[4],
          cate: category,
        };
        awesomeJson[category].push(obj);
      }
    }
  }

  return awesomeJson;
};

var w = new AwesomeWorker('awesome');
plugin((json) => {
  w.save(json);
});
<|endoftext|>


<commit_before>// This patch is used for auto-upgrading when DOM is changed by blaze.
// Well, maybe not just blaze.
// But when you are manipulating the DOM by yourself, you should remember to upgrade.
//
// Browser compatibility: check https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
// also http://caniuse.com/#search=MutationObserver

/*global EnvConfig:true*/
/*global componentHandler:true*/

var MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
if (typeof MutationObserver !== 'function') return;

EnvConfig['blazeFix'] = true;
var myEnv = EnvConfig['patchers']['blaze'] = {};

var handleMutation = function (mutation) {
  switch (mutation.type) {
    case 'attributes':
      //! Try to upgrade this element.
      break;
    case 'characterData':
      // Ignore character changes.
      break;
    case 'childList':
      // Upgrade the new children.
      if (mutation.addedNodes.length > 0 && mutation.target instanceof Element) {
        componentHandler.upgradeElements(mutation.target, true);
      }
      break;
    default:
      throw new Error('Invalid type of mutation.');
  }
};

var mutationObserverBehaviors = {
  'fullUpgrade': function (mutations, observer) {
    componentHandler.upgradeAllRegistered();
  },
  'mutationOnly': function (mutations, observer) {
    for (var i = 0, n = mutations.length; i < n; i++) {
      handleMutation(mutations[i]);
    }
  },
  'none': function (mutations, observer) {}
}
// After some testing, seems like 'fullUpgrade' tends to be more efficient than 'mutationOnly'.
, activeMutationObserverBehaviorName = 'mutationOnly'
, activeMutationObserverBehavior = mutationObserverBehaviors[activeMutationObserverBehaviorName];

// Add public method for switching the mutation observer's behavior.
myEnv.setUpgradeStyle = function (name) {
  if (Object.prototype.hasOwnProperty.call(mutationObserverBehaviors, name)) {
    activeMutationObserverBehaviorName = name;
    activeMutationObserverBehavior = mutationObserverBehaviors[name];
  }
};
myEnv.getUpgradeStyle = function () {
  return activeMutationObserverBehaviorName;
};

var observerLocked = false;
var observer = new MutationObserver(function(mutations, observer) {
  // Use the lock to ignore mutations happened during the process of an update.
  if (observerLocked) return;
  observerLocked = true;

  activeMutationObserverBehavior(mutations, observer);

  observerLocked = false;
}), observing = false, observeConfig = {
    childList: true,
    attributes: true,
    characterData: false,
    subtree: true
//     attributeOldValue: false,
//     characterDataOldValue: false
//     attributeFilter: []
  };

Meteor.startup(function () {
  observer.observe(document.body, observeConfig);
});
<commit_msg>Revert the default to fullUpgrade to avoid issue google/material-design-lite#1083.<commit_after>// This patch is used for auto-upgrading when DOM is changed by blaze.
// Well, maybe not just blaze.
// But when you are manipulating the DOM by yourself, you should remember to upgrade.
//
// Browser compatibility: check https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
// also http://caniuse.com/#search=MutationObserver

/*global EnvConfig:true*/
/*global componentHandler:true*/

var MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
if (typeof MutationObserver !== 'function') return;

EnvConfig['blazeFix'] = true;
var myEnv = EnvConfig['patchers']['blaze'] = {};

var handleMutation = function (mutation) {
  switch (mutation.type) {
    case 'attributes':
      //! Try to upgrade this element.
      break;
    case 'characterData':
      // Ignore character changes.
      break;
    case 'childList':
      // Upgrade the new children.
      if (mutation.addedNodes.length > 0 && mutation.target instanceof Element) {
        componentHandler.upgradeElements(mutation.target, true);
      }
      break;
    default:
      throw new Error('Invalid type of mutation.');
  }
};

var mutationObserverBehaviors = {
  'fullUpgrade': function (mutations, observer) {
    componentHandler.upgradeAllRegistered();
  },
  'mutationOnly': function (mutations, observer) {
    for (var i = 0, n = mutations.length; i < n; i++) {
      handleMutation(mutations[i]);
    }
  },
  'none': function (mutations, observer) {}
}
// After some testing, seems like 'fullUpgrade' tends to be more efficient than 'mutationOnly'.
, activeMutationObserverBehaviorName = 'fullUpgrade'
, activeMutationObserverBehavior = mutationObserverBehaviors[activeMutationObserverBehaviorName];

// Add public method for switching the mutation observer's behavior.
myEnv.setUpgradeStyle = function (name) {
  if (Object.prototype.hasOwnProperty.call(mutationObserverBehaviors, name)) {
    activeMutationObserverBehaviorName = name;
    activeMutationObserverBehavior = mutationObserverBehaviors[name];
  }
};
myEnv.getUpgradeStyle = function () {
  return activeMutationObserverBehaviorName;
};

var observerLocked = false;
var observer = new MutationObserver(function(mutations, observer) {
  // Use the lock to ignore mutations happened during the process of an update.
  if (observerLocked) return;
  observerLocked = true;

  activeMutationObserverBehavior(mutations, observer);

  observerLocked = false;
}), observing = false, observeConfig = {
    childList: true,
    attributes: true,
    characterData: false,
    subtree: true
//     attributeOldValue: false,
//     characterDataOldValue: false
//     attributeFilter: []
  };

Meteor.startup(function () {
  observer.observe(document.body, observeConfig);
});
<|endoftext|>


<commit_before>/*
 * Visualizer superclass
 */
var Visualizer = function(html){
    var self = this;
    self.html = html;

    self.clear = function(){
        throw "Visualizer must implement the clear() method!";
    }

    self.show = function(data){
        throw "Visualizer must implement the show(data) method!";
    }

    return self;
}

/*
 * Main Renderer class
 */
var Renderer = function(player, resolution, frames){
    var self = this;
    var actualPlayer = player
        || null;
    var actualResolution = resolution
        || parseInt($(actualPlayer).data("resolution"))
        || (self.log("Warn: No resolution given or found, defaulting to",30) && 30);
    var actualFrames = frames
        || parseInt($(actualPlayer).data("frames"))
        || 0;

    if(actualPlayer == null) throw "No player given to renderer!";

    self.player = $(actualPlayer);
    self.resolution = actualResolution;
    self.frame = 0;
    self.frames = self.allocateArray(actualFrames);
    self.framesLoaded = 0;
    self.visualizers = {};

    self.updateDisplay = function(){
        var pad = function(thing, count){
            thing = ""+thing;
            count = (count || 2) - thing.length;
            return (count<0)? thing : new Array(count+1).join("0")+thing;
        }
        var displayUnit = $(".seeker", self.player).width()/self.frames.length;
        $(".seeker .loaded", self.player).width(self.framesLoaded*displayUnit+"px");
        $(".seeker .current", self.player).width(self.frame*displayUnit+"px");
        var seconds = self.frame / self.resolution;
        var msecs = self.frame % self.resolution / self.resolution * 1000;
        var secs = Math.floor(seconds) % 60;
        var mins = Math.floor(seconds / 60);
        var text = pad(mins)+":"+pad(secs)+":"+pad(msecs,3);
        $(".frameinfo", self.player).text(text);

        $.each(self.visualizers, function(i, visualizer){
            visualizer.show(self.frames[self.frame]);
        });
        return text;
    }

    self.frameData = function(frame){
        var actframe = frame || self.frame;
        return (actframe<self.framesLoaded && 0<=actframe) ? self.frames[actframe] : null;
    }

    self.setFrame = function(newframe){
        var data = self.frameData(newframe);
        if(data && newframe != self.frame){
            self.frame = newframe;
            self.updateDisplay();
        }
        return self.frame;
    }

    self.setFrameData = function(data){
        self.frames = data;
        self.framesLoaded = data.length;
        self.setFrame(0);
        return self.frames;
    }

    self.addFrameData = function(data){
        for(var i=0; i<data.length; i++){
            self.frames[i+self.framesLoaded] = data[i];
        }
        if(self.framesLoaded == 0 && data.length>0){
            self.frame = 0;
        }
        self.framesLoaded += data.length;
        self.updateDisplay();
        return self.frames;
    }

    var playing = false;
    self.setPlaying = function(val){
        playing = val;
        if(val){
            self.log("Playing");
            $(".play i", self.player).removeClass("fa-play").addClass("fa-pause");
            if(self.frame == self.frames.length-1){
                self.setFrame(0);
            }
        }else{
            self.log("Pausing");
            $(".play i", self.player).addClass("fa-play").removeClass("fa-pause");
        }
        return playing;
    }

    self.isPlaying = function(){
        return playing;
    }

    self.seekerFrame = function(ev){
        var seeker = $(".seeker", self.player);
        var x = ev.pageX - seeker.offset().left;
        return Math.floor(x/seeker.width()*self.frames.length);
    }

    self.loadVisualizer = function(js, html, css){
        // Inject CSS
        var style = $("


<commit_before>exports.techModule = module;

var fs = require('fs'),
    myPath = require('bem/lib/path'),
    Template = require('bem/lib/template');

exports.bemBuild = function(prefixes, outputDir, outputName) {
    var decl = [],
        json = fs.readFileSync(outputDir + outputName + '.bemjson.js');

    try {
        json = require('vm').runInThisContext(json);
    } catch(e) {
        console.log(json);
        throw e;
    }

    iterateJson(json, getBuilder(decl));

    fs.writeFileSync(
        myPath.join(outputDir, outputName + this.getFileSuffix()),
        'exports.blocks = ' + JSON.stringify(mergeDecls([], decl), null, 4),
        'utf-8');
}

/***************************/

// TODO: заменить на require('bem/lib/util').mergeDecls как только доедет https://github.com/bem/bem-tools/commit/99a75c8bb23889f5813ebbacc2514d5e1ae1d971
function mergeDecls(d1, d2) {
    var keys = {};
    d1?
        d1.forEach(function(o) { keys[o.name || o] = o }) :
        d1 = [];

    d2.forEach(function(o2) {
        var name = o2.name || o2;
        if (keys.hasOwnProperty(name)) {
            var o1 = keys[name];
            o2.elems && (o1.elems = mergeDecls(o1.elems, o2.elems));
            o2.mods && (o1.mods = mergeDecls(o1.mods, o2.mods));
            o2.vals && (o1.vals = mergeDecls(o1.vals, o2.vals));
        } else {
            d1.push(o2);
            keys[name] = o2;
        }
    });

    return d1;
}

function isSimple(obj) {
    var t = typeof obj;
    return t === 'string' || t === 'number';
}

function iterateJson(obj, fn) {
    if(obj && !isSimple(obj))
        if(Array.isArray(obj)) {
            var i = 0, l = obj.length;
            while(i < l) iterateJson(obj[i++], fn);
        } else fn(obj);
    return obj;
}

function getBuilder(decl, block) {
    return function(obj) {
        block = obj.block || block;

        obj.block && decl.push({ name: block });

        obj.elem && decl.push({ name: block, elems: [{ name: obj.elem }] });

        var mods;

        if(mods = obj.mods)
            for(var n in mods)
                if(mods.hasOwnProperty(n))
                    decl.push({
                        name: block,
                        mods: [{ name: n, vals: [ mods[n] ] }]
                    });

        if(obj.elem && (mods = obj.elemMods))
            for(var n in mods)
                if(mods.hasOwnProperty(n))
                    decl.push({
                        name: block,
                        elems: [{
                            name: obj.elem,
                            mods: [{ name: n, vals: [ mods[n] ] }]
                        }]
                    });

        iterateJson([obj.mix, obj.content], getBuilder(decl, block));
    }
}
<commit_msg>Using mergeDecls method from utils<commit_after>var util = require('bem/lib/util');

exports.techModule = module;

var fs = require('fs'),
    myPath = require('bem/lib/path'),
    Template = require('bem/lib/template');

exports.bemBuild = function(prefixes, outputDir, outputName) {
    var decl = [],
        json = fs.readFileSync(outputDir + outputName + '.bemjson.js');

    try {
        json = require('vm').runInThisContext(json);
    } catch(e) {
        console.log(json);
        throw e;
    }

    iterateJson(json, getBuilder(decl));

    fs.writeFileSync(
        myPath.join(outputDir, outputName + this.getFileSuffix()),
        'exports.blocks = ' + JSON.stringify(util.mergeDecls([], decl), null, 4),
        'utf-8');
}

function isSimple(obj) {
    var t = typeof obj;
    return t === 'string' || t === 'number';
}

function iterateJson(obj, fn) {
    if(obj && !isSimple(obj))
        if(Array.isArray(obj)) {
            var i = 0, l = obj.length;
            while(i < l) iterateJson(obj[i++], fn);
        } else fn(obj);
    return obj;
}

function getBuilder(decl, block) {
    return function(obj) {
        block = obj.block || block;

        obj.block && decl.push({ name: block });

        obj.elem && decl.push({ name: block, elems: [{ name: obj.elem }] });

        var mods;

        if(mods = obj.mods)
            for(var n in mods)
                if(mods.hasOwnProperty(n))
                    decl.push({
                        name: block,
                        mods: [{ name: n, vals: [ mods[n] ] }]
                    });

        if(obj.elem && (mods = obj.elemMods))
            for(var n in mods)
                if(mods.hasOwnProperty(n))
                    decl.push({
                        name: block,
                        elems: [{
                            name: obj.elem,
                            mods: [{ name: n, vals: [ mods[n] ] }]
                        }]
                    });

        iterateJson([obj.mix, obj.content], getBuilder(decl, block));
    }
}
<|endoftext|>