<commit_before>async function main() {

  const REGEXP = {
    "/ year": /\/ year$/,
    "/ month": /\/ month$/,
    "N open": /^([\d,]+) Open$/,
    "N closed": /^([\d,]+) Closed$/,
    "N files": /^([\d,]+) files?$/,
    "N forks": /^([\d,]+) forks?$/,
    "N stars": /^([\d,]+) stars?$/,
    "N comments": /^([\d,]+) comments?$/,
    "N remaining": /^([\d,]+) remaining$/,
    "N languages": /^([\d,]+) languages?$/,
    "N applications": /^([\d,]+) applications?$/,
    "N repositories": /^([\d,]+) repositor(?:y|ies)$/,
    "N hours ago": /^(\d+) hours ago$/,
    "N stars today": /^([\d,]+) stars today$/,
    "N stars this week": /^([\d,]+) stars this week$/,
    "N stars this month": /^([\d,]+) stars this month$/,
    "N contributions": /^([\d,]+) contributions?$/,
    "N contributions in the last year": /^([\d,]+) contributions in the last year$/,
    "month day": /^(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) (\d+)$/,
    "on month day": /^on (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) (\d+)$/,
    "on month day, year": /^on (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) (\d+), (\d{4})$/,
    "Joined on Jun 24, 2017": /^Joined on (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) (\d+), (\d{4})$/,
    "View USER on GitHub": /View (\w+) on GitHub/,
    "Turn USER into an organization": /Turn ([\w-]+) into an organization/,
    "Your next charge will be on date": /Your next charge will be on (\d{4}-\d{2}-\d{2})./
  }

  let page = where()
  let lang = language()
  let dict = await translation(lang)

  let observer = new MutationObserver(mutations => {
    mutations
      .filter(mutation =>
        (mutation.type === 'childList' && mutation.addedNodes.length) ||
        (mutation.type === 'attributes' && mutation.attributeName === 'placeholder')
      )
      .forEach(mutation => translate(mutation.target))
  })

  observer.observe(document.body, {
    attributes: true,
    childList: true,
    subtree: true
  })

  translate(document.body)

  function find(str) {
    if (page && dict[page][str]) return ` ${dict[page][str]} `
    if (dict.global[str]) return ` ${dict.global[str]} `
    for (let key in REGEXP) {
      let reg = REGEXP[key]
      if (reg.test(str)) {
        str = str
            .replace(reg, dict.regexp[key])
            .replace(/(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)/, (match, p1) => dict.global.m2n[p1])
        return ` ${str} `
      }
    }
    return ''
  }

  function where() {
    if (location.host === 'gist.github.com') return 'gist'

    let pathname = location.pathname
    if (pathname === '/' || /dashboard/.test(pathname)) return 'dashboard'
    if (pathname === '/new') return 'new'
    if (pathname === '/trending') return 'trending'
    if (/^\/watching/.test(pathname)) return 'watching'
    if (/^\/pulls(\/.*)?/.test(pathname)) return 'pulls'
    if (/^\/notifications/.test(pathname)) return 'notifications'
    if (/^\/(?:organizations\/\w+\/)?settings\/.+$/.test(pathname)) return 'settings'
    if (/^\/[\w-]+\/?$/.test(pathname)) return 'profile'
    if (/^\/[\w-]+\/.+$/.test(pathname)) return 'repository'
  }

  function language() {
    return localStorage.getItem('polyocat-lang') || navigator.language || navigator.languages[0]
  }

  async function translation(lang) {
    let url = chrome.extension.getURL(`translations/${lang}.json`)
    let res = await fetch(url)
    return await res.json()
  }

  function translate(node) {
    translateNode(node)

    if (node.classList && node.classList[0] === 'file') {
      return translate(node.firstElementChild)
    }

    if (
      node.id === 'files' ||
      node.id === 'readme' ||
      /octotree_sidebar/.test(node.className) ||
      /personal-access-tokens-group/.test(node.className)
    ) { return }

    node.childNodes.forEach(translate)
  }

  function translateNode(node) {
    if (node.nodeType === Node.TEXT_NODE) return translateTextNode(node)
    if (node.nodeType === Node.ELEMENT_NODE) return translateElement(node)
  }

  function translateTextNode(node) {
    if (!node || node.nodeValue == null) return
    let text = node.nodeValue.replace(/[\n\s]+/g, ' ').trim()
    let tran = find(text)
    if (tran) node.nodeValue = tran
  }

  function translateElement(elem) {
    if (elem.tagName === 'INPUT' || elem.tagName === 'TEXTAREA') translateAttribute(elem, 'placeholder')

    if (elem.tagName === 'INPUT' && elem.type === 'submit') translateAttribute(elem, 'value')

    if (elem.tagName === 'OPTGROUP' && elem.label) translateAttribute(elem, 'label')

    if (elem.tagName === 'RELATIVE-TIME' || elem.tagName === 'TIME-AGO') {
      elem.innerText = elem.innerText.replace(
        /(\d+|a|an) (day|days|hour|hours|minute|minutes|second|seconds|month|months|year|years) (ago)/,
        (match, p1, p2, p3) => `${find(p1) || p1} ${find(p2).trim()}${find(p3).trim()}`
      )
    }

    translateAttribute(elem, 'aria-label')
    translateAttribute(elem, 'data-copied-hint')
  }

  function translateAttribute(elem, name) {
    if (elem[name]) {
      let tran = find(elem[name])
      if (tran) elem[name] = tran
      return
    }

    if (elem.hasAttribute(name)) {
      let tran = find(elem.getAttribute(name))
      if (tran) elem.setAttribute(name, tran)
    }
  }

  console.log(page, lang, dict)
}

main()<commit_msg>Ignore CodeMirror<commit_after>async function main() {

  const REGEXP = {
    "/ year": /\/ year$/,
    "/ month": /\/ month$/,
    "N open": /^([\d,]+) Open$/,
    "N closed": /^([\d,]+) Closed$/,
    "N files": /^([\d,]+) files?$/,
    "N forks": /^([\d,]+) forks?$/,
    "N stars": /^([\d,]+) stars?$/,
    "N comments": /^([\d,]+) comments?$/,
    "N remaining": /^([\d,]+) remaining$/,
    "N languages": /^([\d,]+) languages?$/,
    "N applications": /^([\d,]+) applications?$/,
    "N repositories": /^([\d,]+) repositor(?:y|ies)$/,
    "N hours ago": /^(\d+) hours ago$/,
    "N stars today": /^([\d,]+) stars today$/,
    "N stars this week": /^([\d,]+) stars this week$/,
    "N stars this month": /^([\d,]+) stars this month$/,
    "N contributions": /^([\d,]+) contributions?$/,
    "N contributions in the last year": /^([\d,]+) contributions in the last year$/,
    "month day": /^(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) (\d+)$/,
    "on month day": /^on (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) (\d+)$/,
    "on month day, year": /^on (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) (\d+), (\d{4})$/,
    "Joined on Jun 24, 2017": /^Joined on (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) (\d+), (\d{4})$/,
    "View USER on GitHub": /View ([\w-]+) on GitHub/,
    "Turn USER into an organization": /Turn ([\w-]+) into an organization/,
    "Your next charge will be on date": /Your next charge will be on (\d{4}-\d{2}-\d{2})./
  }

  let page = where()
  let lang = language()
  let dict = await translation(lang)

  let observer = new MutationObserver(mutations => {
    mutations
      .filter(mutation =>
        (mutation.type === 'childList' && mutation.addedNodes.length) ||
        (mutation.type === 'attributes' && mutation.attributeName === 'placeholder')
      )
      .forEach(mutation => translate(mutation.target))
  })

  observer.observe(document.body, {
    attributes: true,
    childList: true,
    subtree: true
  })

  translate(document.body)

  function find(str) {
    if (page && dict[page][str]) return ` ${dict[page][str]} `
    if (dict.global[str]) return ` ${dict.global[str]} `
    for (let key in REGEXP) {
      let reg = REGEXP[key]
      if (reg.test(str)) {
        str = str
            .replace(reg, dict.regexp[key])
            .replace(/(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)/, (match, p1) => dict.global.m2n[p1])
        return ` ${str} `
      }
    }
    return ''
  }

  function where() {
    if (location.host === 'gist.github.com') return 'gist'

    let pathname = location.pathname
    if (pathname === '/' || /dashboard/.test(pathname)) return 'dashboard'
    if (pathname === '/new') return 'new'
    if (pathname === '/trending') return 'trending'
    if (/^\/watching/.test(pathname)) return 'watching'
    if (/^\/pulls(\/.*)?/.test(pathname)) return 'pulls'
    if (/^\/notifications/.test(pathname)) return 'notifications'
    if (/^\/(?:organizations\/\w+\/)?settings\/.+$/.test(pathname)) return 'settings'
    if (/^\/[\w-]+\/?$/.test(pathname)) return 'profile'
    if (/^\/[\w-]+\/.+$/.test(pathname)) return 'repository'
  }

  function language() {
    return localStorage.getItem('polyocat-lang') || navigator.language || navigator.languages[0]
  }

  async function translation(lang) {
    let url = chrome.extension.getURL(`translations/${lang}.json`)
    let res = await fetch(url)
    return await res.json()
  }

  function translate(node) {
    translateNode(node)

    if (node.classList && node.classList[0] === 'file') {
      return translate(node.firstElementChild)
    }

    if (
      node.id === 'files' ||
      node.id === 'readme' ||
      /octotree_sidebar/.test(node.className) ||
      /personal-access-tokens-group/.test(node.className)
    ) { return }

    if (node.className && /CodeMirror/.test(node.className)) return

    node.childNodes.forEach(translate)
  }

  function translateNode(node) {
    if (node.nodeType === Node.TEXT_NODE) return translateTextNode(node)
    if (node.nodeType === Node.ELEMENT_NODE) return translateElement(node)
  }

  function translateTextNode(node) {
    if (!node || node.nodeValue == null) return
    let text = node.nodeValue.replace(/[\n\s]+/g, ' ').trim()
    let tran = find(text)
    if (tran) node.nodeValue = tran
  }

  function translateElement(elem) {
    if (elem.tagName === 'INPUT' || elem.tagName === 'TEXTAREA') translateAttribute(elem, 'placeholder')

    if (elem.tagName === 'INPUT' && elem.type === 'submit') translateAttribute(elem, 'value')

    if (elem.tagName === 'OPTGROUP' && elem.label) translateAttribute(elem, 'label')

    if (elem.tagName === 'RELATIVE-TIME' || elem.tagName === 'TIME-AGO') {
      elem.innerText = elem.innerText.replace(
        /(\d+|a|an) (day|days|hour|hours|minute|minutes|second|seconds|month|months|year|years) (ago)/,
        (match, p1, p2, p3) => `${find(p1) || p1} ${find(p2).trim()}${find(p3).trim()}`
      )
    }

    translateAttribute(elem, 'aria-label')
    translateAttribute(elem, 'data-copied-hint')
  }

  function translateAttribute(elem, name) {
    if (elem[name]) {
      let tran = find(elem[name])
      if (tran) elem[name] = tran
      return
    }

    if (elem.hasAttribute(name)) {
      let tran = find(elem.getAttribute(name))
      if (tran) elem.setAttribute(name, tran)
    }
  }

  console.log(page, lang, dict)
}

main()<|endoftext|>


<commit_before>'use strict';

const req = require('req');

const config = require('../config'),
    templates = require('./templates');

const daysEnum = {
    TODAY: 0,
    TOMORROW: 1,
    IN_2_DAYS: 2,
    IN_3_DAYS: 3,
    IN_4_DAYS: 4,
    IN_5_DAYS: 5,
    IN_6_DAYS: 6,
    IN_7_DAYS: 7,
    IN_8_DAYS: 8
};

const callAPI = async (city, countryCode) => {
    const api = `http://api.openweathermap.org/data/2.5/forecast/daily/?q=${city},${countryCode}&APPID=${config.weatherApiKey}&units=metric&lang=es`;
    const body = await req(api);
    return JSON.parse(body);
};

const getForecast = async (city, countryCode, day) => {
    const result = await callAPI(city, countryCode);
    return day === null ? result.list[daysEnum.TOMORROW] : result.list[day];
};

const getForecastMessage = async (city, countryCode, day) => {
    const forecast = await getForecast(city, countryCode, day);
    let message;
    try {
        message = templates.weatherReport(forecast, city);
    } catch (err) {
        message = templates.errorMessage;
    }

    return message;
};

const getForecastWarnMessage = async (city, countryCode) => {
    const forecast = await getForecast(city, countryCode, daysEnum.TOMORROW);
    const [ weatherCode ] = forecast.weather[0].id.toString();

    let message = null;
    //if (weatherCode === '8' || weatherCode === '9') { // TODO: uncomment this. It's for debugging purpouses.
        try {
            message = templates.weatherReport(forecast, city);
        } catch (err) {
            message = templates.errorMessage;
        }
    //}

    return message;
};

module.exports = { daysEnum, getForecast, getForecastMessage, getForecastWarnMessage };
<commit_msg>:bug: WeatherWarnBot - uncommented some lines.<commit_after>'use strict';

const req = require('req');

const config = require('../config'),
    templates = require('./templates');

const daysEnum = {
    TODAY: 0,
    TOMORROW: 1,
    IN_2_DAYS: 2,
    IN_3_DAYS: 3,
    IN_4_DAYS: 4,
    IN_5_DAYS: 5,
    IN_6_DAYS: 6,
    IN_7_DAYS: 7,
    IN_8_DAYS: 8
};

const callAPI = async (city, countryCode) => {
    const api = `http://api.openweathermap.org/data/2.5/forecast/daily/?q=${city},${countryCode}&APPID=${config.weatherApiKey}&units=metric&lang=es`;
    const body = await req(api);
    return JSON.parse(body);
};

const getForecast = async (city, countryCode, day) => {
    const result = await callAPI(city, countryCode);
    return day === null ? result.list[daysEnum.TOMORROW] : result.list[day];
};

const getForecastMessage = async (city, countryCode, day) => {
    const forecast = await getForecast(city, countryCode, day);
    let message;
    try {
        message = templates.weatherReport(forecast, city);
    } catch (err) {
        message = templates.errorMessage;
    }

    return message;
};

const getForecastWarnMessage = async (city, countryCode) => {
    const forecast = await getForecast(city, countryCode, daysEnum.TOMORROW);
    const [ weatherCode ] = forecast.weather[0].id.toString();

    let message = null;
    if (weatherCode === '8' || weatherCode === '9') {
        try {
            message = templates.weatherReport(forecast, city);
        } catch (err) {
            message = templates.errorMessage;
        }
    }

    return message;
};

module.exports = { daysEnum, getForecast, getForecastMessage, getForecastWarnMessage };
<|endoftext|>


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

$.namespace('azkaban');

azkaban.TimeGraphView = Backbone.View.extend({
	events: {
	},

	initialize: function(settings) {
		this.model.bind('render', this.render, this);
		this.model.bind('change:page', this.render, this);
    this.modelField = settings.modelField;
    this.graphContainer = settings.el;
    this.render();
	},

	render: function(self) {
		var series = this.model.get(this.modelField);
    if (series == null) {
      return;
    }

    // Array of points to be passed to Morris.
    var data = [];

    // Map of y value to index for faster look-up in the lineColorsCallback to
    // get the status for each point.
    var indexMap = {};
	  for (var i = 0; i < series.length; ++i) {
      if (series[i].startTime == null || series[i].endTime == null) {
        console.log("Each element in series must have startTime and endTime");
        return;
      }
      var startTime = series[i].startTime;
      var endTime = series[i].endTime;
      if (endTime == -1) {
        endTime = new Date().getTime();
      }
      var duration = endTime - startTime;
      data.push({
        time: endTime,
        duration: duration
      });

      indexMap[endTime.toString()] = i;
    }

		if (data.length == 0) {
			$(this.graphContainer).hide();
			return;
		}

		var graphDiv = document.createElement('div');
		$(this.graphContainer).html(graphDiv);

    var lineColorsCallback = function(row, sidx, type) {
      if (type != 'point') {
        return "#000000";
      }
      var i = indexMap[row.x.toString()];
      var status = series[i].status;
      if (status == 'SKIPPED') {
        return '#aaa';
      }
      else if (status == 'SUCCEEDED') {
        return '#4e911e';
      }
      else if (status == 'RUNNING') {
        return '#009fc9';
      }
      else if (status == 'PAUSED') {
        return '#c92123';
      }
      else if (status == 'FAILED' ||
          status == 'FAILED_FINISHING' ||
          status == 'KILLED') {
        return '#cc0000';
      }
      else {
        return '#ccc';
      }
    };

    var yLabelFormatCallback = function(y) {
      var seconds = y / 1000.0;
      return seconds.toString() + " s";
    };

    var hoverCallback = function(index, options, content) {
      // Note: series contains the data points in descending order and index
      // is the index into Morris's internal array of data sorted in ascending
      // x order.
      var status = series[options.data.length - index - 1].status;
      return content +
          '
Status: ' + status + '
';
    };

    Morris.Line({
      element: graphDiv,
      data: data,
      xkey: 'time',
      ykeys: ['duration'],
      labels: ['Duration'],
      lineColors: lineColorsCallback,
      yLabelFormat: yLabelFormatCallback,
      hoverCallback: hoverCallback
    });
	}
});
<commit_msg>Issue #180 - Time Graph gracefully handles invalid data points.<commit_after>/*
 * Copyright 2012 LinkedIn Corp.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

$.namespace('azkaban');

azkaban.TimeGraphView = Backbone.View.extend({
	events: {
	},

	initialize: function(settings) {
		this.model.bind('render', this.render, this);
		this.model.bind('change:page', this.render, this);
    this.modelField = settings.modelField;
    this.graphContainer = settings.el;
    this.render();
	},

	render: function(self) {
		var series = this.model.get(this.modelField);
    if (series == null) {
      return;
    }

    // Array of points to be passed to Morris.
    var data = [];

    // Map of y value to index for faster look-up in the lineColorsCallback to
    // get the status for each point.
    var indexMap = {};
	  for (var i = 0; i < series.length; ++i) {
      if (series[i].startTime == null || series[i].endTime == null) {
        console.log("Each element in series must have startTime and endTime");
        return;
      }
      var startTime = series[i].startTime;
      var endTime = series[i].endTime;
      if (startTime == -1 && endTime == -1) {
        console.log("Ignoring data point with both start and end time invalid.");
        continue;
      }

      var duration = 0;
      if (endTime != -1 && startTime != -1) {
        duration = endTime - startTime;
      }
      data.push({
        time: endTime,
        duration: duration
      });

      indexMap[endTime.toString()] = i;
    }

		if (data.length == 0) {
			$(this.graphContainer).hide();
			return;
		}

		var graphDiv = document.createElement('div');
		$(this.graphContainer).html(graphDiv);

    var lineColorsCallback = function(row, sidx, type) {
      if (type != 'point') {
        return "#000000";
      }
      var i = indexMap[row.x.toString()];
      var status = series[i].status;
      if (status == 'SKIPPED') {
        return '#aaa';
      }
      else if (status == 'SUCCEEDED') {
        return '#4e911e';
      }
      else if (status == 'RUNNING') {
        return '#009fc9';
      }
      else if (status == 'PAUSED') {
        return '#c92123';
      }
      else if (status == 'FAILED' ||
          status == 'FAILED_FINISHING' ||
          status == 'KILLED') {
        return '#cc0000';
      }
      else {
        return '#ccc';
      }
    };

    var yLabelFormatCallback = function(y) {
      var seconds = y / 1000.0;
      return seconds.toString() + " s";
    };

    var hoverCallback = function(index, options, content) {
      // Note: series contains the data points in descending order and index
      // is the index into Morris's internal array of data sorted in ascending
      // x order.
      var status = series[options.data.length - index - 1].status;
      return content +
          '
Status: ' + status + '
';
    };

    Morris.Line({
      element: graphDiv,
      data: data,
      xkey: 'time',
      ykeys: ['duration'],
      labels: ['Duration'],
      lineColors: lineColorsCallback,
      yLabelFormat: yLabelFormatCallback,
      hoverCallback: hoverCallback
    });
	}
});
<|endoftext|>


<commit_before>import Defaults from '../constants/defaults';

class Chrome {

    // Panel helpers
    // ============================================================================================

    static getTopSites (done, max = 10) {
        chrome.topSites.get(function (visitedURLs) {
            visitedURLs = visitedURLs.slice(0, parseInt(max, 10));

            let items = [];

            for (let site of visitedURLs) {
                items.push({
                    title : site.title,
                    url   : site.url,
                    img   : `chrome://favicon/${site.url}`
                });
            }

            done(items);
        });
    }

    static getRecentlyClosed (done, max = 10) {
        chrome.sessions.getRecentlyClosed({ maxResults: parseInt(max, 10) }, function (sessions) {
            let items = [];

            for (let session of sessions) {
                if (session.window && session.window.tabs.length === 1) {
                    session.tab = session.window.tabs[0];
                }

                items.push({
                    title   : session.tab ? session.tab.title : `${session.window.tabs.length} Tabs`,
                    session : session.window ? session.window.sessionId : session.tab.sessionId,
                    img     : session.tab ? `chrome://favicon/${session.tab.url}` : null
                });
            }

            done(items);
        });
    }

    static getApps (done) {
        let find128Image = function (icons) {
            for (let icon of icons) {
                if (icon.size === 128) {
                    return icon.url;
                }
            }

            return '/noicon.png';
        };

        chrome.management.getAll(function (list) {
            // Only get active apps (no extensions)
            list = list.filter(function (a) {
                return a.enabled && a.type !== 'extension' && a.type !== 'theme' && a.isApp
            });

            // Sort them alphabetically
            list.sort(function (a, b) {
                if (a.name < b.name)      { return -1; }
                else if (a.name > b.name) { return 1; }
                else                      { return 0; }
            });

            let items = [];

            for (let extInf of list) {
                items.push({
                    title : extInf.name,
                    id    : extInf.id,
                    img   : find128Image(extInf.icons)
                });
            }

            items.push({
                title : 'All apps',
                id    : 'ntp-apps',
                img   : '../assets/img/apps128.png',
                href  : 'chrome://apps'
            });

            items.push({
                title : 'Web store',
                id    : 'ntp-webstore',
                img   : '../assets/img/webstore128.png',
                href  : 'https://chrome.google.com/webstore'
            });

            done(items);
        });
    }

    static getShortcuts (done) {
        let shortcuts = [
            {
                title: 'Bookmarks',
                url: 'chrome://bookmarks/',
                img: 'chrome://favicon/chrome://bookmarks/'
            },
            {
                title: 'History',
                url: 'chrome://history/',
                img: 'chrome://favicon/chrome://history/'
            },
            {
                title: 'Downloads',
                url: 'chrome://downloads/',
                img: 'chrome://favicon/chrome://downloads/'
            },
            {
                title: 'Extensions',
                url: 'chrome://extensions/',
                img: 'chrome://favicon/chrome://extensions/'
            },
            {
                title: 'Settings',
                url: 'chrome://settings/',
                img: 'chrome://favicon/chrome://settings/'
            }
        ];

        done(shortcuts);
    }

    static getDevices (done) {
        chrome.sessions.getDevices(function (devices) {
            let items = [];

            for (let device of devices) {
                let tabs = [];

                for (let session of device.sessions) {
                    let sessionTabs = session.window ? session.window.tabs : [session.tab];

                    for (let tab of sessionTabs) {
                        tabs.push({
                            title : tab.title,
                            url   : tab.url,
                            img   : `chrome://favicon/${tab.url}`
                        });
                    }
                }

                items.push({
                    title : device.deviceName,
                    tabs  : tabs
                });
            }

            done(items);
        });
    }

    // Settings helpers (Chrome extension storage)
    // ============================================================================================

    static getSettings (done) {
        let defaults = Defaults;

        chrome.storage.sync.get(null, (results) => {
            Object.assign(defaults, results);

            done(defaults);
        });
    }

    static setSetting (key, value) {
        let settingObj = {};
        settingObj[key] = value;

        chrome.storage.sync.set(settingObj);

        // Prompt new tab page to fetch new settings
        chrome.runtime.sendMessage({ msg: 'saved' });
    }

    // Fonts
    // ============================================================================================

    static getFonts (done) {
        chrome.fontSettings.getFontList((fonts) => {
            done(fonts);
        });
    }
}

export default Chrome;
<commit_msg>Computed property name<commit_after>import Defaults from '../constants/defaults';

class Chrome {

    // Panel helpers
    // ============================================================================================

    static getTopSites (done, max = 10) {
        chrome.topSites.get(function (visitedURLs) {
            visitedURLs = visitedURLs.slice(0, parseInt(max, 10));

            let items = [];

            for (let site of visitedURLs) {
                items.push({
                    title : site.title,
                    url   : site.url,
                    img   : `chrome://favicon/${site.url}`
                });
            }

            done(items);
        });
    }

    static getRecentlyClosed (done, max = 10) {
        chrome.sessions.getRecentlyClosed({ maxResults: parseInt(max, 10) }, function (sessions) {
            let items = [];

            for (let session of sessions) {
                if (session.window && session.window.tabs.length === 1) {
                    session.tab = session.window.tabs[0];
                }

                items.push({
                    title   : session.tab ? session.tab.title : `${session.window.tabs.length} Tabs`,
                    session : session.window ? session.window.sessionId : session.tab.sessionId,
                    img     : session.tab ? `chrome://favicon/${session.tab.url}` : null
                });
            }

            done(items);
        });
    }

    static getApps (done) {
        let find128Image = function (icons) {
            for (let icon of icons) {
                if (icon.size === 128) {
                    return icon.url;
                }
            }

            return '/noicon.png';
        };

        chrome.management.getAll(function (list) {
            // Only get active apps (no extensions)
            list = list.filter(function (a) {
                return a.enabled && a.type !== 'extension' && a.type !== 'theme' && a.isApp
            });

            // Sort them alphabetically
            list.sort(function (a, b) {
                if (a.name < b.name)      { return -1; }
                else if (a.name > b.name) { return 1; }
                else                      { return 0; }
            });

            let items = [];

            for (let extInf of list) {
                items.push({
                    title : extInf.name,
                    id    : extInf.id,
                    img   : find128Image(extInf.icons)
                });
            }

            items.push({
                title : 'All apps',
                id    : 'ntp-apps',
                img   : '../assets/img/apps128.png',
                href  : 'chrome://apps'
            });

            items.push({
                title : 'Web store',
                id    : 'ntp-webstore',
                img   : '../assets/img/webstore128.png',
                href  : 'https://chrome.google.com/webstore'
            });

            done(items);
        });
    }

    static getShortcuts (done) {
        let shortcuts = [
            {
                title: 'Bookmarks',
                url: 'chrome://bookmarks/',
                img: 'chrome://favicon/chrome://bookmarks/'
            },
            {
                title: 'History',
                url: 'chrome://history/',
                img: 'chrome://favicon/chrome://history/'
            },
            {
                title: 'Downloads',
                url: 'chrome://downloads/',
                img: 'chrome://favicon/chrome://downloads/'
            },
            {
                title: 'Extensions',
                url: 'chrome://extensions/',
                img: 'chrome://favicon/chrome://extensions/'
            },
            {
                title: 'Settings',
                url: 'chrome://settings/',
                img: 'chrome://favicon/chrome://settings/'
            }
        ];

        done(shortcuts);
    }

    static getDevices (done) {
        chrome.sessions.getDevices(function (devices) {
            let items = [];

            for (let device of devices) {
                let tabs = [];

                for (let session of device.sessions) {
                    let sessionTabs = session.window ? session.window.tabs : [session.tab];

                    for (let tab of sessionTabs) {
                        tabs.push({
                            title : tab.title,
                            url   : tab.url,
                            img   : `chrome://favicon/${tab.url}`
                        });
                    }
                }

                items.push({
                    title : device.deviceName,
                    tabs  : tabs
                });
            }

            done(items);
        });
    }

    // Settings helpers (Chrome extension storage)
    // ============================================================================================

    static getSettings (done) {
        let defaults = Defaults;

        chrome.storage.sync.get(null, (results) => {
            Object.assign(defaults, results);

            done(defaults);
        });
    }

    static setSetting (key, value) {
        chrome.storage.sync.set({
            [key]: value
        });

        // Prompt new tab page to fetch new settings
        chrome.runtime.sendMessage({ msg: 'saved' });
    }

    // Fonts
    // ============================================================================================

    static getFonts (done) {
        chrome.fontSettings.getFontList((fonts) => {
            done(fonts);
        });
    }
}

export default Chrome;
<|endoftext|>


<commit_before>var TIMELINE = {};

TIMELINE.generatePostElement = function(post) {
	return $('
')
	.attr({
		title: post.createdAt + '
via ' + post.app.name,
		'data-id': post.id,
		'data-user-id': post.userId,
		'data-user-comment': post.user.comment,
		'data-user-color': post.user.color,
		'data-is-favorited': 'false',
		'data-is-reposted': 'false',
		style: post.isReply ? 'border-color: ' + post.reply.user.color + ';' : ''
	})
	.append(post.isRepostToPost ? generateRepostInformation() : null)
	.append(post.isReply ? generateReplyTo() : null)
	.append(generateArticle(post))
	.append(generateFooter());

	function generateRepostInformation() {
		return $('
')
		.append(generateInfo());

		function generateInfo() {
			return $('
')
			.append($('').attr('href', conf.url + '/' + post.repostedByUser.screenName + '/talk?noheader=true')
				.append(
					$('').attr('src', conf.url + '/img/icon/' + post.repostedByUser.screenName)))
			.append($(''))
			.append($('').attr('href', conf.url + '/' + post.repostedByUser.screenName).text(post.repostedByUser.name))
			.append('によってRepost');
		}
	}

	function generateReplyTo() {
		return $('
')
		.append(generateArticle(post.reply));
	}

	function generateArticle(post) {
		return $('
')
		.append(generateIcon())
		.append(generateHeader())
		.append(generateMain())
		.append(generateFooter());

		function generateIcon() {
			return $('')
			.attr('href', conf.url + '/' + post.user.screenName + '/talk?noheader=true')
			.append(
			$('')
			.attr('src', conf.url + '/img/icon/' + post.user.screenName)
			);
		}

		function generateHeader() {
			return generateHeader()
			.append(generateScreenName())
			.append(generateTime());

			function generateHeader() {
				return $('').prepend($('
').prepend(generateName()));
			}

			function generateName() {
				return $('')
				.attr('href', conf.url + '/' + post.user.screenName)
				.text(escapeHtml(post.user.name));
			}

			function generateScreenName() {
				return $('')
				.text('@' + post.user.screenName);
			}

			function generateTime() {
				return $('')
				.attr('href', conf.url + '/' + post.user.screenName + '/' + post.id)
				.append(
					$('<time>')
					.attr('datetime', post.createdAt)
					.text(post.createdAt));
			}
		}

		function generateMain() {
			return $('
')
			.append($('
').html((post.isReply ? ' ' : '') + parseText(post.text)))
			.append(post.isImageAttached ? generateImage() : null);

			function generateImage() {
				return $('')
				.attr('src', conf.url + '/img/post/' + post.id);
			}
		}

		function generateFooter() {
			return $('')
			.append(generateActions());

			function generateActions() {
				return $('
')
				.append(generateRepost())
				.append(generateFavorite());

				function generateRepost() {
					return $('
')
					.append(generateRepostButton())
					.append($('').attr('href', conf.url + '/' + post.user.screenName + '/' + post.id + '/reposts'));

					function generateRepostButton() {
						return $('<button class="repostButton" title="Repost" role="button">')
						.append($(''));
					}
				}

				function generateFavorite() {
					return $('
')
					.append(generateFavoriteButton())
					.append($('').attr('href', conf.url + '/' + post.user.screenName + '/' + post.id + '/favorites'));

					function generateFavoriteButton() {
						return $('<button class="favoriteButton" title="お気に入り" role="button">')
						.append($(''));
					}
				}
			}
		}
	}

	function generateFooter() {
		return $('')
		.append(generateForm());

		function generateForm() {
			return $('<form class="replyForm">')
			.append(generateTextArea())
			.append(generateInReplyToPostId())
			.append(generateSubmitButton())
			.append(generateImageAttacher());

			function generateTextArea() {
				return $('<textarea name="text">')
				.text('@' + post.user.screenName + ' ');
			}

			function generateInReplyToPostId() {
				return $('<input name="in_reply_to_post_id" type="hidden">')
				.attr('value', post.id);
			}

			function generateSubmitButton() {
				return $('<input type="submit" value="Reply">');
			}

			function generateImageAttacher() {
				return $('
')
				.append($('

'))
				.append($('<input name="image" type="file" accept="image/*">'));
			}
		}
	}

	function parseText(text) {
		text = escapeHtml(text);
		text = parseURL(text);
		text = parseReply(text);
		text = parseBold(text);
		text = parseSmall(text);
		text = parseNewLine(text);
		return text;

		function parseURL(text) {
			return text.replace(/https?:\/\/[-_.!~*a-zA-Z0-9;\/?:\@&=+\$,%#]+/g, function(url) {
				return '' + url + '';
			});
		}

		function parseReply(text) {
			return text.replace(/@([a-zA-Z0-9_]+)/g, function(_, screenName) {
				return '@' + screenName + '';
			});
		}

		function parseBold(text) {
			return text.replace(/\*\*(.+?)\*\*/g, function(_, word) {
				return '' + word + '';
			});
		}

		function parseSmall(text) {
			return text.replace(/\(\((.+?)\)\)/g, function(_, word) {
				return '(' + word + ')';
			});
		}

		function parseNewLine(text) {
			return text.replace(/(\r\n|\r|\n)/g, '
');
		}
	}

	function escapeHtml(text) {
		return $('
').text(text).html();
	}
}

TIMELINE.setEventPost = function($post) {
	$post.children('article').children('a').click(function() {
		var $content = $("").attr({ src: $post.children('article').children('a').attr('href'), seamless: true });
		openWindow($content, '' + $post.children('article').children('header').children('h2').children('a').text(), 300, 450);
		return false;
	});

	$post.find('.replyForm').submit(function(event) {
		event.preventDefault();
		var $form = $(this);
		var $submitButton = $form.find('[type=submit]');
		$submitButton.attr('disabled', true);
		$.ajax({
			url: 'https://api.misskey.xyz/post/create',
			type: 'post',
			data: new FormData($form[0]),
			processData: false,
			contentType: false,
			dataType: 'json',
			xhrFields: {
				withCredentials: true
			}
		}).done(function() {
			$submitButton.attr('disabled', false);
			$form.text('送信しました');
		}).fail(function() {
			$submitButton.attr('disabled', false);
		});
	});

	$post.find('.imageAttacher input[name=image]').change(function() {
		var $input = $(this);
		var file = $(this).prop('files')[0];
		if (!file.type.match('image.*')) return;
		var reader = new FileReader();
		reader.onload = function() {
			var $img = $('').attr('src', reader.result);
			$input.parent('.imageAttacher').find('p, img').remove();
			$input.parent('.imageAttacher').append($img);
		};
		reader.readAsDataURL(file);
	});

	$post.find('article > footer > .actions > .favorite > .favoriteButton').click(function() {
		var $button = $(this);
		$button.attr('disabled', true);

		if ($post.attr('data-is-favorited') == 'true') {
			$.ajax('https://api.misskey.xyz/post/unfavorite', {
				type: 'delete',
				data: { 'post_id': $post.attr('data-id') },
				dataType: 'json',
				xhrFields: { withCredentials: true }
			}).done(function() {
				$button.attr('disabled', false);
				$post.attr('data-is-favorited', 'false')
			}).fail(function() {
				$button.attr('disabled', false);
			});
		} else {
			$.ajax('https://api.misskey.xyz/post/favorite', {
				type: 'post',
				data: { 'post_id': $post.attr('data-id') },
				dataType: 'json',
				xhrFields: { withCredentials: true }
			}).done(function() {
				$button.attr('disabled', false);
				$post.attr('data-is-favorited', 'true')
			}).fail(function() {
				$button.attr('disabled', false);
			});
		}
	});

	$post.find('article > footer > .actions > .repost > .repostButton').click(function() {
		var $button = $(this);
		$button.attr('disabled', true);

		if ($post.attr('data-is-reposted') == 'true') {
			$.ajax('https://api.misskey.xyz/post/unrepost', {
				type: 'delete',
				data: { 'post_id': $post.attr('data-id') },
				dataType: 'json',
				xhrFields: { withCredentials: true }
			}).done(function() {
				$button.attr('disabled', false);
				$post.attr('data-is-reposted', 'false')
			}).fail(function() {
				$button.attr('disabled', false);
			});
		} else {
			$.ajax('https://api.misskey.xyz/post/repost', {
				type: 'post',
				data: { 'post_id': $post.attr('data-id') },
				dataType: 'json',
				xhrFields: { withCredentials: true }
			}).done(function() {
				$button.attr('disabled', false);
				$post.attr('data-is-reposted', 'true')
			}).fail(function() {
				$button.attr('disabled', false);
			});
		}
	});

	$post.click(function(event) {
		if ($(event.target).is('input') ||
		$(event.target).is('textarea') ||
		$(event.target).is('button') ||
		$(event.target).is('i') ||
		$(event.target).is('a')) return;
		if (document.getSelection().toString() == '') {
			if ($(this).children('footer').css('display') === 'none') {
				$('.timeline > .posts > .post > footer').each(function() {
					$(this).hide(200);
				});
				$(this).children('footer').show(200);
				var text = $(this).find('footer .replyForm textarea').val();
				$(this).find('footer .replyForm textarea').val('');
				$(this).find('footer .replyForm textarea').focus().val(text);
			} else {
				$(this).children('footer').hide(200);
			}
		}
	});

	/*
	$post.dblclick(function (event) {
	if ($(event.target).is('input') || $(event.target).is('textarea')) return;
	window.open('https://misskey.xyz/' + $(this).attr('data-user-screen_name') + '/post/' + $(this).attr('data-id'));
	});
	*/

}

$(function() {
	$('.timeline .post').each(function() {
		TIMELINE.setEventPost($(this));
	});
});<commit_msg>Fix typo<commit_after>var TIMELINE = {};

TIMELINE.generatePostElement = function(post) {
	return $('
')
	.attr({
		title: post.createdAt + '
via ' + post.app.name,
		'data-id': post.id,
		'data-user-id': post.userId,
		'data-user-comment': post.user.comment,
		'data-user-color': post.user.color,
		'data-is-favorited': 'false',
		'data-is-reposted': 'false',
		style: post.isReply ? 'border-color: ' + post.reply.user.color + ';' : ''
	})
	.append(post.isRepostToPost ? generateRepostInformation() : null)
	.append(post.isReply ? generateReplyTo() : null)
	.append(generateArticle(post))
	.append(generateFooter());

	function generateRepostInformation() {
		return $('
')
		.append(generateInfo());

		function generateInfo() {
			return $('
')
			.append($('').attr('href', conf.url + '/' + post.repostedByUser.screenName + '/talk?noheader=true')
				.append(
					$('').attr('src', conf.url + '/img/icon/' + post.repostedByUser.screenName)))
			.append($(''))
			.append($('').attr('href', conf.url + '/' + post.repostedByUser.screenName).text(post.repostedByUser.name))
			.append('によってRepost');
		}
	}

	function generateReplyTo() {
		return $('
')
		.append(generateArticle(post.reply));
	}

	function generateArticle(post) {
		return $('
')
		.append(generateIcon())
		.append(generateHeader())
		.append(generateMain())
		.append(generateFooter());

		function generateIcon() {
			return $('')
			.attr('href', conf.url + '/' + post.user.screenName + '/talk?noheader=true')
			.append(
			$('')
			.attr('src', conf.url + '/img/icon/' + post.user.screenName)
			);
		}

		function generateHeader() {
			return generateHeader()
			.append(generateScreenName())
			.append(generateTime());

			function generateHeader() {
				return $('').prepend($('
').prepend(generateName()));
			}

			function generateName() {
				return $('')
				.attr('href', conf.url + '/' + post.user.screenName)
				.text(escapeHtml(post.user.name));
			}

			function generateScreenName() {
				return $('')
				.text('@' + post.user.screenName);
			}

			function generateTime() {
				return $('')
				.attr('href', conf.url + '/' + post.user.screenName + '/' + post.id)
				.append(
					$('<time>')
					.attr('datetime', post.createdAt)
					.text(post.createdAt));
			}
		}

		function generateMain() {
			return $('
')
			.append($('
').html((post.isReply ? ' ' : '') + parseText(post.text)))
			.append(post.isImageAttached ? generateImage() : null);

			function generateImage() {
				return $('')
				.attr('src', conf.url + '/img/post/' + post.id);
			}
		}

		function generateFooter() {
			return $('')
			.append(generateActions());

			function generateActions() {
				return $('
')
				.append(generateRepost())
				.append(generateFavorite());

				function generateRepost() {
					return $('
')
					.append(generateRepostButton())
					.append($('').attr('href', conf.url + '/' + post.user.screenName + '/' + post.id + '/reposts'));

					function generateRepostButton() {
						return $('<button class="repostButton" title="Repost" role="button">')
						.append($(''));
					}
				}

				function generateFavorite() {
					return $('
')
					.append(generateFavoriteButton())
					.append($('').attr('href', conf.url + '/' + post.user.screenName + '/' + post.id + '/favorites'));

					function generateFavoriteButton() {
						return $('<button class="favoriteButton" title="お気に入り" role="button">')
						.append($(''));
					}
				}
			}
		}
	}

	function generateFooter() {
		return $('')
		.append(generateForm());

		function generateForm() {
			return $('<form class="replyForm">')
			.append(generateTextArea())
			.append(generateInReplyToPostId())
			.append(generateSubmitButton())
			.append(generateImageAttacher());

			function generateTextArea() {
				return $('<textarea name="text">')
				.text('@' + post.user.screenName + ' ');
			}

			function generateInReplyToPostId() {
				return $('<input name="in_reply_to_post_id" type="hidden">')
				.attr('value', post.id);
			}

			function generateSubmitButton() {
				return $('<input type="submit" value="Reply">');
			}

			function generateImageAttacher() {
				return $('
')
				.append($('

'))
				.append($('<input name="image" type="file" accept="image/*">'));
			}
		}
	}

	function parseText(text) {
		text = escapeHtml(text);
		text = parseURL(text);
		text = parseReply(text);
		text = parseBold(text);
		text = parseSmall(text);
		text = parseNewLine(text);
		return text;

		function parseURL(text) {
			return text.replace(/https?:\/\/[-_.!~*a-zA-Z0-9;\/?:\@&=+\$,%#]+/g, function(url) {
				return '' + url + '';
			});
		}

		function parseReply(text) {
			return text.replace(/@([a-zA-Z0-9_]+)/g, function(_, screenName) {
				return '@' + screenName + '';
			});
		}

		function parseBold(text) {
			return text.replace(/\*\*(.+?)\*\*/g, function(_, word) {
				return '' + word + '';
			});
		}

		function parseSmall(text) {
			return text.replace(/\(\((.+?)\)\)/g, function(_, word) {
				return '(' + word + ')';
			});
		}

		function parseNewLine(text) {
			return text.replace(/(\r\n|\r|\n)/g, '
');
		}
	}

	function escapeHtml(text) {
		return $('
').text(text).html();
	}
}

TIMELINE.setEventPost = function($post) {
	$post.children('article').children('a').click(function() {
		var $content = $("").attr({ src: $post.children('article').children('a').attr('href'), seamless: true });
		openWindow($content, '' + $post.children('article').children('header').children('h2').children('a').text(), 300, 450);
		return false;
	});

	$post.find('.replyForm').submit(function(event) {
		event.preventDefault();
		var $form = $(this);
		var $submitButton = $form.find('[type=submit]');
		$submitButton.attr('disabled', true);
		$.ajax({
			url: 'https://api.misskey.xyz/post/create',
			type: 'post',
			data: new FormData($form[0]),
			processData: false,
			contentType: false,
			dataType: 'json',
			xhrFields: {
				withCredentials: true
			}
		}).done(function() {
			$submitButton.attr('disabled', false);
			$form.text('送信しました');
		}).fail(function() {
			$submitButton.attr('disabled', false);
		});
	});

	$post.find('.imageAttacher input[name=image]').change(function() {
		var $input = $(this);
		var file = $(this).prop('files')[0];
		if (!file.type.match('image.*')) return;
		var reader = new FileReader();
		reader.onload = function() {
			var $img = $('').attr('src', reader.result);
			$input.parent('.imageAttacher').find('p, img').remove();
			$input.parent('.imageAttacher').append($img);
		};
		reader.readAsDataURL(file);
	});

	$post.find('article > footer > .actions > .favorite > .favoriteButton').click(function() {
		var $button = $(this);
		$button.attr('disabled', true);

		if ($post.attr('data-is-favorited') == 'true') {
			$.ajax('https://api.misskey.xyz/post/unfavorite', {
				type: 'delete',
				data: { 'post_id': $post.attr('data-id') },
				dataType: 'json',
				xhrFields: { withCredentials: true }
			}).done(function() {
				$button.attr('disabled', false);
				$post.attr('data-is-favorited', 'false')
			}).fail(function() {
				$button.attr('disabled', false);
			});
		} else {
			$.ajax('https://api.misskey.xyz/post/favorite', {
				type: 'post',
				data: { 'post_id': $post.attr('data-id') },
				dataType: 'json',
				xhrFields: { withCredentials: true }
			}).done(function() {
				$button.attr('disabled', false);
				$post.attr('data-is-favorited', 'true')
			}).fail(function() {
				$button.attr('disabled', false);
			});
		}
	});

	$post.find('article > footer > .actions > .repost > .repostButton').click(function() {
		var $button = $(this);
		$button.attr('disabled', true);

		if ($post.attr('data-is-reposted') == 'true') {
			$.ajax('https://api.misskey.xyz/post/unrepost', {
				type: 'delete',
				data: { 'post_id': $post.attr('data-id') },
				dataType: 'json',
				xhrFields: { withCredentials: true }
			}).done(function() {
				$button.attr('disabled', false);
				$post.attr('data-is-reposted', 'false')
			}).fail(function() {
				$button.attr('disabled', false);
			});
		} else {
			$.ajax('https://api.misskey.xyz/post/repost', {
				type: 'post',
				data: { 'post_id': $post.attr('data-id') },
				dataType: 'json',
				xhrFields: { withCredentials: true }
			}).done(function() {
				$button.attr('disabled', false);
				$post.attr('data-is-reposted', 'true')
			}).fail(function() {
				$button.attr('disabled', false);
			});
		}
	});

	$post.click(function(event) {
		if ($(event.target).is('input') ||
		$(event.target).is('textarea') ||
		$(event.target).is('button') ||
		$(event.target).is('i') ||
		$(event.target).is('a')) return;
		if (document.getSelection().toString() == '') {
			if ($(this).children('footer').css('display') === 'none') {
				$('.timeline > .posts > .post > footer').each(function() {
					$(this).hide(200);
				});
				$(this).children('footer').show(200);
				var text = $(this).find('footer .replyForm textarea').val();
				$(this).find('footer .replyForm textarea').val('');
				$(this).find('footer .replyForm textarea').focus().val(text);
			} else {
				$(this).children('footer').hide(200);
			}
		}
	});

	/*
	$post.dblclick(function (event) {
	if ($(event.target).is('input') || $(event.target).is('textarea')) return;
	window.open('https://misskey.xyz/' + $(this).attr('data-user-screen_name') + '/post/' + $(this).attr('data-id'));
	});
	*/

}

$(function() {
	$('.timeline .post').each(function() {
		TIMELINE.setEventPost($(this));
	});
});<|endoftext|>


<commit_before>/*
 * Orange angular-swagger-ui - v0.1.5
 *
 * (C) 2015 Orange, all right reserved
 * MIT Licensed
 */
'use strict';

angular
	.module('swaggerUi')
	.service('swaggerClient', ['$q', '$http', function($q, $http) {
		function formatXml(xml) {
			var formatted = '';
			var reg = /(>)(<)(\/*)/g;
			xml = xml.replace(reg, '$1\r\n$2$3');
			var pad = 0;
			angular.forEach(xml.split('\r\n'), function (node, index) {
				var indent = 0;
				console.log("printing node...")
				console.log(node);
				if (node.match(/.+<\/\w[^>]*>$/)) {
					indent = 0;
				} else if (node.match(/^<\/\w/)) {
					if (pad != 0) {
						pad -= 1;
					}
				} else if (node.match(/^<\w[^>]*[^\/]>.*$/)) {
					indent = 1;
				} else {
					indent = 0;
				}

				var padding = '';
				for (var i = 0; i < pad; i++) {
					padding += '  ';
				}

				formatted += padding + node + '\r\n';
				pad += indent;
			});

			return formatted;
		}

		function formatResult(deferred, data, status, headers, config) {
			var query = '';
			if (config.params) {
				var parts = [];
				for (var key in config.params) {
					parts.push(key + '=' + encodeURIComponent(config.params[key]));
				}
				if (parts.length > 0) {
					query = '?' + parts.join('&');
				}
			}
			deferred.resolve({
				url: config.url + query,
				response: {
					body: data ? ((headers.Accept = 'application/xml') ? formatXml(data) : angular.toJson(data, true)) : 'no content',
					status: status,
					headers: angular.toJson(headers(), true)
				}
			});
		}

		this.send = function(swagger, operation, values, transform) {
			var deferred = $q.defer(),
				query = {},
				headers = {},
				path = operation.path;

			// build request parameters
			for (var i = 0, params = operation.parameters || [], l = params.length; i < l; i++) {
				//TODO manage 'collectionFormat' (csv etc.) !!
				var param = params[i],
					value = values[param.name];

				switch (param.in) {
					case 'query':
						if (!!value) {
							query[param.name] = value;
						}
						break;
					case 'path':
						path = path.replace('{' + param.name + '}', encodeURIComponent(value));
						break;
					case 'header':
						if (!!value) {
							headers[param.name] = value;
						}
						break;
					case 'formData':
						values.body = values.body || new FormData();
						if (!!value) {
							if (param.type === 'file') {
								values.contentType = undefined; // make browser defining it by himself
							}
							values.body.append(param.name, value);
						}
						break;
				}
			}

			// add headers
			headers.Accept = values.responseType;
			headers['Content-Type'] = values.body ? values.contentType : 'text/plain';

			// build request
			//FIXME should use server hosting the documentation if scheme or host are not defined
			var request = {
					method: operation.httpMethod,
					url: [swagger.schemes && swagger.schemes[0] || 'http', '://', swagger.host, swagger.basePath || '', path].join(''),
					headers: headers,
					data: values.body,
					params: query
				},
				callback = function(data, status, headers, config) {
					formatResult(deferred, data, status, headers, config);
				};

			// apply transform
			if (typeof transform === 'function') {
				transform(request);
			}

			// send request
			$http(request)
				.success(callback)
				.error(callback);

			return deferred.promise;
		};

	}]);<commit_msg>remove console.logs<commit_after>/*
 * Orange angular-swagger-ui - v0.1.5
 *
 * (C) 2015 Orange, all right reserved
 * MIT Licensed
 */
'use strict';

angular
	.module('swaggerUi')
	.service('swaggerClient', ['$q', '$http', function($q, $http) {
		function formatXml(xml) {
			var formatted = '';
			var reg = /(>)(<)(\/*)/g;
			xml = xml.replace(reg, '$1\r\n$2$3');
			var pad = 0;
			angular.forEach(xml.split('\r\n'), function (node, index) {
				var indent = 0;
				if (node.match(/.+<\/\w[^>]*>$/)) {
					indent = 0;
				} else if (node.match(/^<\/\w/)) {
					if (pad != 0) {
						pad -= 1;
					}
				} else if (node.match(/^<\w[^>]*[^\/]>.*$/)) {
					indent = 1;
				} else {
					indent = 0;
				}

				var padding = '';
				for (var i = 0; i < pad; i++) {
					padding += '  ';
				}

				formatted += padding + node + '\r\n';
				pad += indent;
			});

			return formatted;
		}

		function formatResult(deferred, data, status, headers, config) {
			var query = '';
			if (config.params) {
				var parts = [];
				for (var key in config.params) {
					parts.push(key + '=' + encodeURIComponent(config.params[key]));
				}
				if (parts.length > 0) {
					query = '?' + parts.join('&');
				}
			}
			deferred.resolve({
				url: config.url + query,
				response: {
					body: data ? ((headers.Accept = 'application/xml') ? formatXml(data) : angular.toJson(data, true)) : 'no content',
					status: status,
					headers: angular.toJson(headers(), true)
				}
			});
		}

		this.send = function(swagger, operation, values, transform) {
			var deferred = $q.defer(),
				query = {},
				headers = {},
				path = operation.path;

			// build request parameters
			for (var i = 0, params = operation.parameters || [], l = params.length; i < l; i++) {
				//TODO manage 'collectionFormat' (csv etc.) !!
				var param = params[i],
					value = values[param.name];

				switch (param.in) {
					case 'query':
						if (!!value) {
							query[param.name] = value;
						}
						break;
					case 'path':
						path = path.replace('{' + param.name + '}', encodeURIComponent(value));
						break;
					case 'header':
						if (!!value) {
							headers[param.name] = value;
						}
						break;
					case 'formData':
						values.body = values.body || new FormData();
						if (!!value) {
							if (param.type === 'file') {
								values.contentType = undefined; // make browser defining it by himself
							}
							values.body.append(param.name, value);
						}
						break;
				}
			}

			// add headers
			headers.Accept = values.responseType;
			headers['Content-Type'] = values.body ? values.contentType : 'text/plain';

			// build request
			//FIXME should use server hosting the documentation if scheme or host are not defined
			var request = {
					method: operation.httpMethod,
					url: [swagger.schemes && swagger.schemes[0] || 'http', '://', swagger.host, swagger.basePath || '', path].join(''),
					headers: headers,
					data: values.body,
					params: query
				},
				callback = function(data, status, headers, config) {
					formatResult(deferred, data, status, headers, config);
				};

			// apply transform
			if (typeof transform === 'function') {
				transform(request);
			}

			// send request
			$http(request)
				.success(callback)
				.error(callback);

			return deferred.promise;
		};

	}]);<|endoftext|>


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

/* Helper method to add all request parameters to a usecase */
function passRequestParameters(flowHelper, usecase) {
	var names = cocoon.request.getParameterNames();
	while (names.hasMoreElements()) {
	    var name = names.nextElement();
		if (!name.equals("lenya.usecase")
			&& !name.equals("lenya.continuation")
			&& !name.equals("submit")) {

			var value = flowHelper.getRequest(cocoon).get(name);

			var string = new Packages.java.lang.String();
			if (string.getClass().isInstance(value)) {
				usecase.setParameter(name, value);
			}
			else {
				usecase.setPart(name, value);
			}

		}
	}
}

function selectView(usecaseName) {
	var usecaseView = new Packages.java.lang.String(usecaseName).replace('.', '/');
	var view = "view/" + usecaseView;
	return view;
}

/* Returns the query string to attach to the target URL. This is used in the site area. */
function getTargetQueryString(usecaseName) {
	var isTabUsecase = new Packages.java.lang.String(usecaseName).startsWith('tab');
	var queryString = "";
	if (isTabUsecase) {
		queryString = "?lenya.usecase=" + usecaseName;
	}
	return queryString;
}

function executeUsecase() {
	var usecaseName = cocoon.request.getParameter("lenya.usecase");
	var usecaseResolver = cocoon.getComponent("org.apache.lenya.cms.usecase.UsecaseResolver");
	var usecase = usecaseResolver.resolve(usecaseName);

	var flowHelper = cocoon.getComponent("org.apache.lenya.cms.cocoon.flow.FlowHelper");
	var request = flowHelper.getRequest(cocoon);
	var sourceUrl = Packages.org.apache.lenya.util.ServletHelper.getWebappURI(request);
	usecase.setSourceURL(sourceUrl);

	usecase.setName(usecaseName);

	var success = false;

	passRequestParameters(flowHelper, usecase);
	usecase.checkPreconditions();

    if (usecase.isInteractive()) {
		var view = selectView(usecaseName);
		var ready = false;
		while (!ready) {

			cocoon.sendPageAndWait(view, {
			    "usecase" : usecase
			});

			passRequestParameters(flowHelper, usecase);
			usecase.advance();

			if (cocoon.request.getParameter("submit")) {
				usecase.checkExecutionConditions();
				if (usecase.getErrorMessages().isEmpty()) {
					usecase.execute();
					if (usecase.getErrorMessages().isEmpty()) {
						usecase.checkPostconditions();
						if (usecase.getErrorMessages().isEmpty()) {
							ready = true;
							success = true;
						}
					}
				}
			}
			else if (cocoon.request.getParameter("cancel")) {
				ready = true;
			}
		}
	}
	else {
		usecase.execute();
		if (usecase.getErrorMessages().isEmpty()) {
			usecase.checkPostconditions();
			if (usecase.getErrorMessages().isEmpty()) {
				success = true;
			}
		}
	}

	var url = request.getContextPath() + usecase.getTargetURL(success) + getTargetQueryString(usecaseName);
	cocoon.redirectTo(url);

}
<commit_msg>release use case component after usage (does not solve the case where user never completes second step in interactive use cases)<commit_after>/*
 * Copyright  1999-2004 The Apache Software Foundation
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */

/* Helper method to add all request parameters to a usecase */
function passRequestParameters(flowHelper, usecase) {
	var names = cocoon.request.getParameterNames();
	while (names.hasMoreElements()) {
	    var name = names.nextElement();
		if (!name.equals("lenya.usecase")
			&& !name.equals("lenya.continuation")
			&& !name.equals("submit")) {

			var value = flowHelper.getRequest(cocoon).get(name);

			var string = new Packages.java.lang.String();
			if (string.getClass().isInstance(value)) {
				usecase.setParameter(name, value);
			}
			else {
				usecase.setPart(name, value);
			}

		}
	}
}

function selectView(usecaseName) {
	var usecaseView = new Packages.java.lang.String(usecaseName).replace('.', '/');
	var view = "view/" + usecaseView;
	return view;
}

/* Returns the query string to attach to the target URL. This is used in the site area. */
function getTargetQueryString(usecaseName) {
	var isTabUsecase = new Packages.java.lang.String(usecaseName).startsWith('tab');
	var queryString = "";
	if (isTabUsecase) {
		queryString = "?lenya.usecase=" + usecaseName;
	}
	return queryString;
}

function executeUsecase() {
	var usecaseName = cocoon.request.getParameter("lenya.usecase");
	var usecaseResolver = cocoon.getComponent("org.apache.lenya.cms.usecase.UsecaseResolver");
	var usecase = usecaseResolver.resolve(usecaseName);

	var flowHelper = cocoon.getComponent("org.apache.lenya.cms.cocoon.flow.FlowHelper");
	var request = flowHelper.getRequest(cocoon);
	var sourceUrl = Packages.org.apache.lenya.util.ServletHelper.getWebappURI(request);
	usecase.setSourceURL(sourceUrl);

	usecase.setName(usecaseName);

	var success = false;

	passRequestParameters(flowHelper, usecase);
	usecase.checkPreconditions();

    if (usecase.isInteractive()) {
		var view = selectView(usecaseName);
		var ready = false;
		while (!ready) {

			cocoon.sendPageAndWait(view, {
			    "usecase" : usecase
			});

			passRequestParameters(flowHelper, usecase);
			usecase.advance();

			if (cocoon.request.getParameter("submit")) {
				usecase.checkExecutionConditions();
				if (usecase.getErrorMessages().isEmpty()) {
					usecase.execute();
					if (usecase.getErrorMessages().isEmpty()) {
						usecase.checkPostconditions();
						if (usecase.getErrorMessages().isEmpty()) {
							ready = true;
							success = true;
						}
					}
				}
			}
			else if (cocoon.request.getParameter("cancel")) {
				ready = true;
			}
		}
	}
	else {
		usecase.execute();
		if (usecase.getErrorMessages().isEmpty()) {
			usecase.checkPostconditions();
			if (usecase.getErrorMessages().isEmpty()) {
				success = true;
			}
		}
	}

	var url = request.getContextPath() + usecase.getTargetURL(success) + getTargetQueryString(usecaseName);

        /* done with usecase component, tell usecaseResolver to release it */
        usecaseResolver.release(usecase);

	cocoon.redirectTo(url);

}
<|endoftext|>


<commit_before>var cdb = require('cartodb.js');
var CategoryColors = require('./category-colors');
var AutoStyler = cdb.core.Model.extend({
  initialize: function (dataviewModel, options) {
    this.options = options || {};
    this.dataviewModel = dataviewModel;
    this.colors = new CategoryColors();
    this.layer = this.dataviewModel.layer;
    this.STYLE_TEMPLATE = this.options.basemap === 'DARK' ? AutoStyler.STYLE_TEMPLATE_DARK : AutoStyler.STYLE_TEMPLATE_LIGHT;
  },

  _getLayerHeader: function (symbol) {
    return '#' + this.dataviewModel.layer.get('layer_name').replace(/\s*/g, '') + '[mapnik-geometry-type=' + AutoStyler.MAPNIK_MAPPING[symbol] + ']{';
  },

  getPreservedWidth: function () {
    var originalWidth;
    var startingStyle = this.layer.get && this.layer.get('cartocss');
    if (startingStyle) {
      originalWidth = startingStyle.match(/marker-width:.*?;\s/g);
      if (originalWidth) {
        if (originalWidth.length > 1) {
          return null;
        } else {
          originalWidth = originalWidth[0].trim().replace('marker-width:', '').replace(';', '');
        }
      }
    }
    return originalWidth;
  }

});

// for Light Basemap
AutoStyler.STYLE_TEMPLATE_LIGHT = {
  polygon: ['{{layername}}',
          '  polygon-fill: {{defaultColor}};',
          '  polygon-opacity: 0.9;  ',
          '  polygon-gamma: 0.5;    ',
          '  line-color: #fff;',
          '  line-width: 0.25;',
          '  line-opacity: 0.25;',
          '  line-comp-op: hard-light;',
          '  {{ramp}}',
          '}'].join('\n'),
  marker: ['{{layername}}',
         '  marker-width: {{markerWidth}};',
         '  marker-fill-opacity: 0.9;  ',
         '  marker-fill: {{defaultColor}};  ',
         '  marker-line-color: #fff;',
         '  marker-allow-overlap: true;',
         '  marker-line-width: 1;',
         '  marker-line-opacity: 0.8;',
         '  {{ramp}}',
         '}'].join('\n'),
  line: ['{{layername}}',
          '  line-color: {{defaultColor}};',
          '  line-width: 0.3;',
          '  line-opacity: 0.3;',
          '  {{ramp}}',
          '}'].join('\n')
};

// for Dark Basemap
AutoStyler.STYLE_TEMPLATE_DARK = {
  polygon: ['{{layername}}',
          '  polygon-fill: {{defaultColor}};',
          '  polygon-opacity: 0.9;  ',
          '  polygon-gamma: 0.5;    ',
          '  line-color: #fff;',
          '  line-width: 0.25;',
          '  line-opacity: 0.25;',
          '  line-comp-op: hard-light;',
          '  {{ramp}}',
          '}'].join('\n'),
  marker: ['{{layername}}',
        '  marker-width: {{markerWidth}};',
        '  marker-fill-opacity: 0.9;  ',
        '  marker-fill: {{defaultColor}};  ',
        '  marker-line-color: #000;',
        '  marker-allow-overlap: true;',
        '  marker-line-width: 1;',
        '  marker-line-opacity: 0.5;',
        '  {{ramp}}',
         '}'].join('\n'),
  line: ['{{layername}}',
          '  line-color: {{defaultColor}};',
          '  line-width: 0.3;',
          '  line-opacity: 0.3;',
          '  {{ramp}}',
          '}'].join('\n')
};

AutoStyler.MAPNIK_MAPPING = {
  polygon: 3,
  marker: 1,
  line: 2
};

module.exports = AutoStyler;
<commit_msg>adds the worst regex ever<commit_after>var cdb = require('cartodb.js');
var CategoryColors = require('./category-colors');
var AutoStyler = cdb.core.Model.extend({
  initialize: function (dataviewModel, options) {
    this.options = options || {};
    this.dataviewModel = dataviewModel;
    this.colors = new CategoryColors();
    this.layer = this.dataviewModel.layer;
    this.STYLE_TEMPLATE = this.options.basemap === 'DARK' ? AutoStyler.STYLE_TEMPLATE_DARK : AutoStyler.STYLE_TEMPLATE_LIGHT;
  },

  _getLayerHeader: function (symbol) {
    return '#' + this.dataviewModel.layer.get('layer_name').replace(/\s*/g, '') + '[mapnik-geometry-type=' + AutoStyler.MAPNIK_MAPPING[symbol] + ']{';
  },

  getPreservedWidth: function () {
    var originalWidth;
    var startingStyle = this.layer.get && (this.layer.get('cartocss') || this.layer.get('meta').cartocss);
    if (startingStyle) {
      originalWidth = startingStyle.match(/marker-width:.*?;\s/g);
      if (originalWidth) {
        if (originalWidth.length > 1) {
          var variableWidths = startingStyle.match(/\[.*?[><=].*?].*?{\s*?marker\-width\:\s*?\d.*?;\s*?}/g).join('\n');
          return {
            ramp: variableWidths,
            fixed: originalWidth[0].trim().replace('marker-width:', '').replace(';', '')
          };
        } else {
          originalWidth = originalWidth[0].trim().replace('marker-width:', '').replace(';', '');
        }
      }
    }
    return originalWidth;
  }

});

// for Light Basemap
AutoStyler.STYLE_TEMPLATE_LIGHT = {
  polygon: ['{{layername}}',
          '  polygon-fill: {{defaultColor}};',
          '  polygon-opacity: 0.9;  ',
          '  polygon-gamma: 0.5;    ',
          '  line-color: #fff;',
          '  line-width: 0.25;',
          '  line-opacity: 0.25;',
          '  line-comp-op: hard-light;',
          '  {{ramp}}',
          '}'].join('\n'),
  marker: ['{{layername}}',
         '  marker-width: {{markerWidth}};',
         '  marker-fill-opacity: 0.9;  ',
         '  marker-fill: {{defaultColor}};  ',
         '  marker-line-color: #fff;',
         '  marker-allow-overlap: true;',
         '  marker-line-width: 1;',
         '  marker-line-opacity: 0.8;',
         '  {{ramp}}',
         '  {{wramp}}',
         '}'].join('\n'),
  line: ['{{layername}}',
          '  line-color: {{defaultColor}};',
          '  line-width: 0.3;',
          '  line-opacity: 0.3;',
          '  {{ramp}}',
          '}'].join('\n')
};

// for Dark Basemap
AutoStyler.STYLE_TEMPLATE_DARK = {
  polygon: ['{{layername}}',
          '  polygon-fill: {{defaultColor}};',
          '  polygon-opacity: 0.9;  ',
          '  polygon-gamma: 0.5;    ',
          '  line-color: #fff;',
          '  line-width: 0.25;',
          '  line-opacity: 0.25;',
          '  line-comp-op: hard-light;',
          '  {{ramp}}',
          '}'].join('\n'),
  marker: ['{{layername}}',
        '  marker-width: {{markerWidth}};',
        '  marker-fill-opacity: 0.9;  ',
        '  marker-fill: {{defaultColor}};  ',
        '  marker-line-color: #000;',
        '  marker-allow-overlap: true;',
        '  marker-line-width: 1;',
        '  marker-line-opacity: 0.5;',
        '  {{ramp}}',
        '  {{wramp}}',
         '}'].join('\n'),
  line: ['{{layername}}',
          '  line-color: {{defaultColor}};',
          '  line-width: 0.3;',
          '  line-opacity: 0.3;',
          '  {{ramp}}',
          '}'].join('\n')
};

AutoStyler.MAPNIK_MAPPING = {
  polygon: 3,
  marker: 1,
  line: 2
};

module.exports = AutoStyler;
<|endoftext|>


<commit_before>var _ = require('underscore');
var formatter = require('../../formatter');
var HistogramTitleView = require('./histogram-title-view');
var WidgetContent = require('../standard/widget-content-view');
var WidgetViewModel = require('../widget-content-model');
var HistogramChartView = require('./chart');
var placeholder = require('./placeholder.tpl');
var template = require('./content.tpl');
var DropdownView = require('../dropdown/widget-dropdown-view');
var AnimateValues = require('../animate-values.js');
var animationTemplate = require('./animation-template.tpl');

/**
 * Widget content view for a histogram
 */
module.exports = WidgetContent.extend({
  defaults: {
    chartHeight: 48 + 20 + 4
  },

  events: {
    'click .js-clear': '_clear',
    'click .js-zoom': '_zoom'
  },

  initialize: function () {
    this.model = this.options.dataModel;
    this.viewModel = new WidgetViewModel();
    this.lockedByUser = false;
    WidgetContent.prototype.initialize.call(this);
  },

  _initViews: function () {
    var titleView = new HistogramTitleView({
      viewModel: this.viewModel,
      dataModel: this.model
    });

    this.$('.js-title').html(titleView.render().el);
    this.addView(titleView);

    var dropdown = new DropdownView({
      target: this.$('.js-actions'),
      container: this.$('.js-header')
    });

    dropdown.bind('click', function (action) {
      if (action === 'toggle') {
        this.model.toggleCollapsed();
      }
    }, this);

    this.addView(dropdown);

    this._renderMiniChart();
    this._renderMainChart();
  },

  _initBinds: function () {
    this.model.once('change:data', this._onFirstLoad, this);
    this.model.bind('change:collapsed', function (mdl, isCollapsed) {
      this.$el.toggleClass('is-collapsed', !!isCollapsed);
    }, this);
  },

  _onFirstLoad: function () {
    this.render();
    this._storeBounds();

    this.model.bind('change', this._onChangeModel, this);
    this.model._fetch();
  },

  _storeBounds: function () {
    var data = this.model.getData();
    if (data && data.length > 0) {
      this.start = data[0].start;
      this.end = data[data.length - 1].end;
      this.binsCount = data.length;
      this.model.set({ start: this.start, end: this.end, bins: this.binsCount });
    }
  },

  _isZoomed: function () {
    return this.viewModel.get('zoomed');
  },

  _onChangeModel: function () {
    // When the histogram is zoomed, we don't need to rely
    // on the change url to update the histogram
    if (this.model.changed.url && this._isZoomed()) {
      return;
    }

    // if the action was initiated by the user
    // don't replace the stored data
    if (this.lockedByUser) {
      this.lockedByUser = false;
    } else {
      if (this._isZoomed()) {
        this.zoomedData = this.model.getData();
      } else {
        this.histogramChartView.showShadowBars();
        this.originalData = this.model.getData();
      }
      this.histogramChartView.replaceData(this.model.getData());
    }

    if (this.unsettingRange) {
      this._unsetRange();
    } else {
      if (this._isZoomed() && !this.lockZoomedData) {
        this.lockZoomedData = true;
        this.zoomedData = this.model.getData();
      }
    }

    this._updateStats();
  },

  render: function () {
    this.clearSubViews();

    var data = this.model.getData();
    var isDataEmpty = _.isEmpty(data) || _.size(data) === 0;

    this.$el.html(
      template({
        title: this.model.get('title'),
        itemsCount: !isDataEmpty ? data.length : '-'
      })
    );

    if (isDataEmpty) {
      this._addPlaceholder();
    } else {
      this.originalData = this.model.getData();
      this._setupBindings();
      this._initViews();
    }

    return this;
  },

  _unsetRange: function () {
    this.unsettingRange = false;
    this.histogramChartView.replaceData(this.originalData);
    this.viewModel.set({ lo_index: null, hi_index: null });

    if (!this._isZoomed()) {
      this.histogramChartView.showShadowBars();
    }
  },

  _addPlaceholder: function () {
    this.$('.js-content').append(placeholder());
  },

  _renderMainChart: function () {
    this.histogramChartView = new HistogramChartView(({
      margin: { top: 4, right: 4, bottom: 4, left: 4 },
      hasShadowBards: true,
      hasHandles: true,
      hasAxisTip: true,
      width: this.canvasWidth,
      height: this.defaults.chartHeight,
      data: this.model.getData(),
      shadowData: this.model.getData()
    }));

    this.$('.js-content').append(this.histogramChartView.el);
    this.addView(this.histogramChartView);

    this.histogramChartView.bind('range_updated', this._onRangeUpdated, this);
    this.histogramChartView.bind('on_brush_end', this._onBrushEnd, this);
    this.histogramChartView.bind('hover', this._onValueHover, this);
    this.histogramChartView.render().show();

    this._updateStats();
  },

  _renderMiniChart: function () {
    this.miniHistogramChartView = new HistogramChartView(({
      className: 'CDB-Chart--mini',
      margin: { top: 0, right: 0, bottom: 4, left: 4 },
      height: 40,
      showOnWidthChange: false,
      data: this.model.getData()
    }));

    this.addView(this.miniHistogramChartView);
    this.$('.js-content').append(this.miniHistogramChartView.el);
    this.miniHistogramChartView.bind('on_brush_end', this._onMiniRangeUpdated, this);
    this.miniHistogramChartView.render();
  },

  _setupBindings: function () {
    this.viewModel.bind('change:zoomed', this._onChangeZoomed, this);
    this.viewModel.bind('change:zoom_enabled', this._onChangeZoomEnabled, this);
    this.viewModel.bind('change:filter_enabled', this._onChangeFilterEnabled, this);
    this.viewModel.bind('change:total', this._onChangeTotal, this);
    this.viewModel.bind('change:nulls', this._onChangeNulls, this);
    this.viewModel.bind('change:max', this._onChangeMax, this);
    this.viewModel.bind('change:min', this._onChangeMin, this);
    this.viewModel.bind('change:avg', this._onChangeAvg, this);
  },

  _clearTooltip: function () {
    this.$('.js-tooltip').stop().hide();
  },

  _onValueHover: function (info) {
    var $tooltip = this.$('.js-tooltip');

    if (info && info.data) {
      var bottom = this.defaults.chartHeight + 3 - info.top;

      $tooltip.css({ bottom: bottom, left: info.left });
      $tooltip.text(info.data);
      $tooltip.css({ left: info.left - $tooltip.width() / 2 });
      $tooltip.fadeIn(70);
    } else {
      this._clearTooltip();
    }
  },

  _onMiniRangeUpdated: function (loBarIndex, hiBarIndex) {
    this.lockedByUser = false;
    this.lockZoomedData = false;

    this._clearTooltip();
    this.histogramChartView.removeSelection();

    var data = this.originalData;

    if (loBarIndex >= 0 && loBarIndex < data.length && (hiBarIndex - 1) >= 0 && (hiBarIndex - 1) < data.length) {
      this.filter.setRange(
        data[loBarIndex].start,
        data[hiBarIndex - 1].end
      );
      this._updateStats();
    } else {
      console.error('Error accessing array bounds', loBarIndex, hiBarIndex, data);
    }
  },

  _onBrushEnd: function (loBarIndex, hiBarIndex) {
    var data = this._getData();

    if (!data || !data.length) {
      return;
    }

    if (this._isZoomed()) {
      this.lockedByUser = true;
    }

    var properties = { filter_enabled: true, lo_index: loBarIndex, hi_index: hiBarIndex };

    if (!this.viewModel.get('zoomed')) {
      properties.zoom_enabled = true;
    }

    this.viewModel.set(properties);

    if (loBarIndex >= 0 && loBarIndex < data.length && (hiBarIndex - 1) >= 0 && (hiBarIndex - 1) < data.length) {
      this.filter.setRange(
        data[loBarIndex].start,
        data[hiBarIndex - 1].end
      );
      this._updateStats();
    } else {
      console.error('Error accessing array bounds', loBarIndex, hiBarIndex, data);
    }
  },

  _onRangeUpdated: function (loBarIndex, hiBarIndex) {
    var self = this;
    if (this.viewModel.get('zoomed')) {
      this.viewModel.set({ zoom_enabled: false, lo_index: loBarIndex, hi_index: hiBarIndex });
    } else {
      this.viewModel.set({ lo_index: loBarIndex, hi_index: hiBarIndex });
    }

    var updateStats = _.debounce(function () { self._updateStats(); }, 400);
    updateStats();
  },

  _getData: function () {
    var data = this.model.getData();

    if (this._isZoomed()) {
      data = this.zoomedData;
    }
    return data;
  },

  _onChangeFilterEnabled: function () {
    this.$('.js-filter').toggleClass('is-hidden', !this.viewModel.get('filter_enabled'));
  },

  _onChangeZoomEnabled: function () {
    this.$('.js-zoom').toggleClass('is-hidden', !this.viewModel.get('zoom_enabled'));
  },

  _changeHeaderValue: function (className, what, suffix) {
    if (this.viewModel.get(what) === undefined) {
      this.$(className).text('0 ' + suffix);
      return;
    }

    this._addTitleForValue(className, what, suffix);

    var animator = new AnimateValues({
      el: this.$el
    });

    animator.animateValue(this.viewModel, what, className, animationTemplate, {
      formatter: formatter.formatNumber,
      templateData: { suffix: ' ' + suffix }
    });
  },

  _onChangeNulls: function () {
    this._changeHeaderValue('.js-nulls', 'nulls', 'NULL ROWS');
  },

  _onChangeTotal: function () {
    this._changeHeaderValue('.js-val', 'total', 'SELECTED');
  },

  _onChangeMax: function () {
    this._changeHeaderValue('.js-max', 'max', 'MAX');
  },

  _onChangeMin: function () {
    this._changeHeaderValue('.js-min', 'min', 'MIN');
  },

  _onChangeAvg: function () {
    this._changeHeaderValue('.js-avg', 'avg', 'AVG');
  },

  _addTitleForValue: function (className, what, unit) {
    this.$(className).attr('title', this._formatNumberWithCommas(this.viewModel.get(what).toFixed(2)) + ' ' + unit);
  },

  _formatNumberWithCommas: function (x) {
    return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
  },

  _updateStats: function () {
    var data = this.originalData;

    if (this._isZoomed()) {
      data = this.zoomedData;
    }

    var nulls = this.model.get('nulls');

    var min, max;

    if (data && data.length) {
      var loBarIndex = this.viewModel.get('lo_index') || 0;
      var hiBarIndex = this.viewModel.get('hi_index') || data.length;

      var sum = this._calcSum(data, loBarIndex, hiBarIndex);
      var avg = this._calcAvg(data, loBarIndex, hiBarIndex);

      if (loBarIndex >= 0 && loBarIndex < data.length) {
        min = data[loBarIndex].start;
      }

      if (hiBarIndex >= 0 && hiBarIndex - 1 < data.length) {
        max = data[hiBarIndex - 1].end;
      }

      this.viewModel.set({ total: sum, nulls: nulls, min: min, max: max, avg: avg });
    }
  },

  _calcAvg: function (data, start, end) {
    var selectedData = data.slice(start, end);

    var total = this._calcSum(data, start, end, total);

    if (!total) {
      return 0;
    }

    var area = _.reduce(selectedData, function (memo, d) {
      return (d.avg && d.freq) ? (d.avg * d.freq) + memo : memo;
    }, 0);

    return area / total;
  },

  _calcSum: function (data, start, end) {
    return _.reduce(data.slice(start, end), function (memo, d) {
      return d.freq + memo;
    }, 0);
  },

  _onChangeZoomed: function () {
    if (this.viewModel.get('zoomed')) {
      this._onZoomIn();
    } else {
      this._onZoomOut();
    }
  },

  _onZoomIn: function () {
    this._showMiniRange();
    this.histogramChartView.expand(20);

    this.histogramChartView.removeShadowBars();

    this.model.set({ start: null, end: null, bins: null, own_filter: 1 });
    this.model._fetch();
    this.lockedByUser = false;
  },

  _zoom: function () {
    this.lockedByUser = true;
    this.viewModel.set({ zoomed: true, zoom_enabled: false });
    this.histogramChartView.removeSelection();
  },

  _onZoomOut: function () {
    this.lockedByUser = true;
    this.lockZoomedData = false;
    this.unsettingRange = true;

    this.model.set({ start: this.start, end: this.end, bins: this.binsCount, own_filter: null });

    this.viewModel.set({ zoom_enabled: false, filter_enabled: false, lo_index: null, hi_index: null });

    this.filter.unsetRange();

    this.histogramChartView.contract(this.defaults.chartHeight);
    this.histogramChartView.resetIndexes();

    this.miniHistogramChartView.hide();
  },

  _showMiniRange: function () {
    var loBarIndex = this.viewModel.get('lo_index');
    var hiBarIndex = this.viewModel.get('hi_index');

    this.miniHistogramChartView.selectRange(loBarIndex, hiBarIndex);
    this.miniHistogramChartView.show();
  },

  _clear: function () {
    this.histogramChartView.removeSelection();
    this.viewModel.set({ zoomed: false, zoom_enabled: false });
    this.viewModel.trigger('change:zoomed');
  }
});
<commit_msg>Uses same style as in the formula widget<commit_after>var _ = require('underscore');
var formatter = require('../../formatter');
var HistogramTitleView = require('./histogram-title-view');
var WidgetContent = require('../standard/widget-content-view');
var WidgetViewModel = require('../widget-content-model');
var HistogramChartView = require('./chart');
var placeholder = require('./placeholder.tpl');
var template = require('./content.tpl');
var DropdownView = require('../dropdown/widget-dropdown-view');
var AnimateValues = require('../animate-values.js');
var animationTemplate = require('./animation-template.tpl');

/**
 * Widget content view for a histogram
 */
module.exports = WidgetContent.extend({
  defaults: {
    chartHeight: 48 + 20 + 4
  },

  events: {
    'click .js-clear': '_clear',
    'click .js-zoom': '_zoom'
  },

  initialize: function () {
    this.model = this.options.dataModel;
    this.viewModel = new WidgetViewModel();
    this.lockedByUser = false;
    WidgetContent.prototype.initialize.call(this);
  },

  _initViews: function () {
    var titleView = new HistogramTitleView({
      viewModel: this.viewModel,
      dataModel: this.model
    });

    this.$('.js-title').html(titleView.render().el);
    this.addView(titleView);

    var dropdown = new DropdownView({
      target: this.$('.js-actions'),
      container: this.$('.js-header')
    });

    dropdown.bind('click', function (action) {
      if (action === 'toggle') {
        this.model.toggleCollapsed();
      }
    }, this);

    this.addView(dropdown);

    this._renderMiniChart();
    this._renderMainChart();
  },

  _initBinds: function () {
    this.model.once('change:data', this._onFirstLoad, this);
    this.model.bind('change:collapsed', this.render, this);
  },

  _onFirstLoad: function () {
    this.render();
    this._storeBounds();

    this.model.bind('change', this._onChangeModel, this);
    this.model._fetch();
  },

  _storeBounds: function () {
    var data = this.model.getData();
    if (data && data.length > 0) {
      this.start = data[0].start;
      this.end = data[data.length - 1].end;
      this.binsCount = data.length;
      this.model.set({ start: this.start, end: this.end, bins: this.binsCount });
    }
  },

  _isZoomed: function () {
    return this.viewModel.get('zoomed');
  },

  _onChangeModel: function () {
    // When the histogram is zoomed, we don't need to rely
    // on the change url to update the histogram
    if (this.model.changed.url && this._isZoomed()) {
      return;
    }

    // if the action was initiated by the user
    // don't replace the stored data
    if (this.lockedByUser) {
      this.lockedByUser = false;
    } else {
      if (this._isZoomed()) {
        this.zoomedData = this.model.getData();
      } else {
        this.histogramChartView.showShadowBars();
        this.originalData = this.model.getData();
      }
      this.histogramChartView.replaceData(this.model.getData());
    }

    if (this.unsettingRange) {
      this._unsetRange();
    } else {
      if (this._isZoomed() && !this.lockZoomedData) {
        this.lockZoomedData = true;
        this.zoomedData = this.model.getData();
      }
    }

    this._updateStats();
  },

  render: function () {
    this.clearSubViews();

    var data = this.model.getData();
    var isDataEmpty = _.isEmpty(data) || _.size(data) === 0;

    this.$el.html(
      template({
        title: this.model.get('title'),
        itemsCount: !isDataEmpty ? data.length : '-'
      })
    );

    if (isDataEmpty) {
      this._addPlaceholder();
    } else {
      this.originalData = this.model.getData();
      this._setupBindings();
      var isCollapsed = this.model.isCollapsed();
      this.$el.toggleClass('is-collapsed', !!isCollapsed);
      this._initViews();
    }

    return this;
  },

  _unsetRange: function () {
    this.unsettingRange = false;
    this.histogramChartView.replaceData(this.originalData);
    this.viewModel.set({ lo_index: null, hi_index: null });

    if (!this._isZoomed()) {
      this.histogramChartView.showShadowBars();
    }
  },

  _addPlaceholder: function () {
    this.$('.js-content').append(placeholder());
  },

  _renderMainChart: function () {
    this.histogramChartView = new HistogramChartView(({
      margin: { top: 4, right: 4, bottom: 4, left: 4 },
      hasShadowBards: true,
      hasHandles: true,
      hasAxisTip: true,
      width: this.canvasWidth,
      height: this.defaults.chartHeight,
      data: this.model.getData(),
      shadowData: this.model.getData()
    }));

    this.$('.js-content').append(this.histogramChartView.el);
    this.addView(this.histogramChartView);

    this.histogramChartView.bind('range_updated', this._onRangeUpdated, this);
    this.histogramChartView.bind('on_brush_end', this._onBrushEnd, this);
    this.histogramChartView.bind('hover', this._onValueHover, this);
    this.histogramChartView.render().show();

    this._updateStats();
  },

  _renderMiniChart: function () {
    this.miniHistogramChartView = new HistogramChartView(({
      className: 'CDB-Chart--mini',
      margin: { top: 0, right: 0, bottom: 4, left: 4 },
      height: 40,
      showOnWidthChange: false,
      data: this.model.getData()
    }));

    this.addView(this.miniHistogramChartView);
    this.$('.js-content').append(this.miniHistogramChartView.el);
    this.miniHistogramChartView.bind('on_brush_end', this._onMiniRangeUpdated, this);
    this.miniHistogramChartView.render();
  },

  _setupBindings: function () {
    this.viewModel.bind('change:zoomed', this._onChangeZoomed, this);
    this.viewModel.bind('change:zoom_enabled', this._onChangeZoomEnabled, this);
    this.viewModel.bind('change:filter_enabled', this._onChangeFilterEnabled, this);
    this.viewModel.bind('change:total', this._onChangeTotal, this);
    this.viewModel.bind('change:nulls', this._onChangeNulls, this);
    this.viewModel.bind('change:max', this._onChangeMax, this);
    this.viewModel.bind('change:min', this._onChangeMin, this);
    this.viewModel.bind('change:avg', this._onChangeAvg, this);
  },

  _clearTooltip: function () {
    this.$('.js-tooltip').stop().hide();
  },

  _onValueHover: function (info) {
    var $tooltip = this.$('.js-tooltip');

    if (info && info.data) {
      var bottom = this.defaults.chartHeight + 3 - info.top;

      $tooltip.css({ bottom: bottom, left: info.left });
      $tooltip.text(info.data);
      $tooltip.css({ left: info.left - $tooltip.width() / 2 });
      $tooltip.fadeIn(70);
    } else {
      this._clearTooltip();
    }
  },

  _onMiniRangeUpdated: function (loBarIndex, hiBarIndex) {
    this.lockedByUser = false;
    this.lockZoomedData = false;

    this._clearTooltip();
    this.histogramChartView.removeSelection();

    var data = this.originalData;

    if (loBarIndex >= 0 && loBarIndex < data.length && (hiBarIndex - 1) >= 0 && (hiBarIndex - 1) < data.length) {
      this.filter.setRange(
        data[loBarIndex].start,
        data[hiBarIndex - 1].end
      );
      this._updateStats();
    } else {
      console.error('Error accessing array bounds', loBarIndex, hiBarIndex, data);
    }
  },

  _onBrushEnd: function (loBarIndex, hiBarIndex) {
    var data = this._getData();

    if (!data || !data.length) {
      return;
    }

    if (this._isZoomed()) {
      this.lockedByUser = true;
    }

    var properties = { filter_enabled: true, lo_index: loBarIndex, hi_index: hiBarIndex };

    if (!this.viewModel.get('zoomed')) {
      properties.zoom_enabled = true;
    }

    this.viewModel.set(properties);

    if (loBarIndex >= 0 && loBarIndex < data.length && (hiBarIndex - 1) >= 0 && (hiBarIndex - 1) < data.length) {
      this.filter.setRange(
        data[loBarIndex].start,
        data[hiBarIndex - 1].end
      );
      this._updateStats();
    } else {
      console.error('Error accessing array bounds', loBarIndex, hiBarIndex, data);
    }
  },

  _onRangeUpdated: function (loBarIndex, hiBarIndex) {
    var self = this;
    if (this.viewModel.get('zoomed')) {
      this.viewModel.set({ zoom_enabled: false, lo_index: loBarIndex, hi_index: hiBarIndex });
    } else {
      this.viewModel.set({ lo_index: loBarIndex, hi_index: hiBarIndex });
    }

    var updateStats = _.debounce(function () { self._updateStats(); }, 400);
    updateStats();
  },

  _getData: function () {
    var data = this.model.getData();

    if (this._isZoomed()) {
      data = this.zoomedData;
    }
    return data;
  },

  _onChangeFilterEnabled: function () {
    this.$('.js-filter').toggleClass('is-hidden', !this.viewModel.get('filter_enabled'));
  },

  _onChangeZoomEnabled: function () {
    this.$('.js-zoom').toggleClass('is-hidden', !this.viewModel.get('zoom_enabled'));
  },

  _changeHeaderValue: function (className, what, suffix) {
    if (this.viewModel.get(what) === undefined) {
      this.$(className).text('0 ' + suffix);
      return;
    }

    this._addTitleForValue(className, what, suffix);

    var animator = new AnimateValues({
      el: this.$el
    });

    animator.animateValue(this.viewModel, what, className, animationTemplate, {
      formatter: formatter.formatNumber,
      templateData: { suffix: ' ' + suffix }
    });
  },

  _onChangeNulls: function () {
    this._changeHeaderValue('.js-nulls', 'nulls', 'NULL ROWS');
  },

  _onChangeTotal: function () {
    this._changeHeaderValue('.js-val', 'total', 'SELECTED');
  },

  _onChangeMax: function () {
    this._changeHeaderValue('.js-max', 'max', 'MAX');
  },

  _onChangeMin: function () {
    this._changeHeaderValue('.js-min', 'min', 'MIN');
  },

  _onChangeAvg: function () {
    this._changeHeaderValue('.js-avg', 'avg', 'AVG');
  },

  _addTitleForValue: function (className, what, unit) {
    this.$(className).attr('title', this._formatNumberWithCommas(this.viewModel.get(what).toFixed(2)) + ' ' + unit);
  },

  _formatNumberWithCommas: function (x) {
    return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
  },

  _updateStats: function () {
    var data = this.originalData;

    if (this._isZoomed()) {
      data = this.zoomedData;
    }

    var nulls = this.model.get('nulls');

    var min, max;

    if (data && data.length) {
      var loBarIndex = this.viewModel.get('lo_index') || 0;
      var hiBarIndex = this.viewModel.get('hi_index') || data.length;

      var sum = this._calcSum(data, loBarIndex, hiBarIndex);
      var avg = this._calcAvg(data, loBarIndex, hiBarIndex);

      if (loBarIndex >= 0 && loBarIndex < data.length) {
        min = data[loBarIndex].start;
      }

      if (hiBarIndex >= 0 && hiBarIndex - 1 < data.length) {
        max = data[hiBarIndex - 1].end;
      }

      this.viewModel.set({ total: sum, nulls: nulls, min: min, max: max, avg: avg });
    }
  },

  _calcAvg: function (data, start, end) {
    var selectedData = data.slice(start, end);

    var total = this._calcSum(data, start, end, total);

    if (!total) {
      return 0;
    }

    var area = _.reduce(selectedData, function (memo, d) {
      return (d.avg && d.freq) ? (d.avg * d.freq) + memo : memo;
    }, 0);

    return area / total;
  },

  _calcSum: function (data, start, end) {
    return _.reduce(data.slice(start, end), function (memo, d) {
      return d.freq + memo;
    }, 0);
  },

  _onChangeZoomed: function () {
    if (this.viewModel.get('zoomed')) {
      this._onZoomIn();
    } else {
      this._onZoomOut();
    }
  },

  _onZoomIn: function () {
    this._showMiniRange();
    this.histogramChartView.expand(20);

    this.histogramChartView.removeShadowBars();

    this.model.set({ start: null, end: null, bins: null, own_filter: 1 });
    this.model._fetch();
    this.lockedByUser = false;
  },

  _zoom: function () {
    this.lockedByUser = true;
    this.viewModel.set({ zoomed: true, zoom_enabled: false });
    this.histogramChartView.removeSelection();
  },

  _onZoomOut: function () {
    this.lockedByUser = true;
    this.lockZoomedData = false;
    this.unsettingRange = true;

    this.model.set({ start: this.start, end: this.end, bins: this.binsCount, own_filter: null });

    this.viewModel.set({ zoom_enabled: false, filter_enabled: false, lo_index: null, hi_index: null });

    this.filter.unsetRange();

    this.histogramChartView.contract(this.defaults.chartHeight);
    this.histogramChartView.resetIndexes();

    this.miniHistogramChartView.hide();
  },

  _showMiniRange: function () {
    var loBarIndex = this.viewModel.get('lo_index');
    var hiBarIndex = this.viewModel.get('hi_index');

    this.miniHistogramChartView.selectRange(loBarIndex, hiBarIndex);
    this.miniHistogramChartView.show();
  },

  _clear: function () {
    this.histogramChartView.removeSelection();
    this.viewModel.set({ zoomed: false, zoom_enabled: false });
    this.viewModel.trigger('change:zoomed');
  }
});
<|endoftext|>


<commit_before>/**
  A view to display a site setting with edit controls

  @class SiteSettingView
  @extends Discourse.View
  @namespace Discourse
  @module Discourse
**/
Discourse.SiteSettingView = Discourse.View.extend({
  classNameBindings: [':row', ':setting', 'content.overridden'],

  templateName: function() {

    // If we're editing a boolean, return a different template
    if (this.get('content.type') === 'bool') return 'admin/templates/site_settings/setting_bool';

    // If we're editing an enum field, show a dropdown
    if (this.get('content.type') === 'enum' ) return 'admin/templates/site_settings/setting_enum';

    // Default to string editor
    return 'admin/templates/site_settings/setting_string';

  }.property('content.type')

});
<commit_msg>Scroll to top on Site-Setting category render<commit_after>/**
  A view to display a site setting with edit controls

  @class SiteSettingView
  @extends Discourse.View
  @namespace Discourse
  @module Discourse
**/
Discourse.SiteSettingView = Discourse.View.extend(Discourse.ScrollTop, {
  classNameBindings: [':row', ':setting', 'content.overridden'],

  templateName: function() {

    // If we're editing a boolean, return a different template
    if (this.get('content.type') === 'bool') return 'admin/templates/site_settings/setting_bool';

    // If we're editing an enum field, show a dropdown
    if (this.get('content.type') === 'enum' ) return 'admin/templates/site_settings/setting_enum';

    // Default to string editor
    return 'admin/templates/site_settings/setting_string';

  }.property('content.type')

});
<|endoftext|>