<commit_before>/**
 * Class for handeling graph plotting of pulse data.
 * Still needs loads of work and is only a cut/paste from template for now
 */
var plot;
var GraphPlotter = {
    choiceContainer: null,

    findMinMaxAvg: function (datasets, from, to) {
        var avgs = {};
        for (key in datasets) {
            var avg = { start: null, end: null, sum: 0, num: 0, avg: null, min: null, max: null, incline: null, decline: null, length: null };
            var lastAltitude = null;
            var label = datasets[key].label;
            for (l in datasets[key].data) {
                var pos = datasets[key].data[l][0];
                if (from != null && (pos >= from && pos <= to)) {
                    if (avg.start == null)
                        avg.start = pos;
                    avg.end = pos;

                    var value = datasets[key].data[l][1];
                    if (avg.min == null)
                        avg.min = value;
                    else
                        avg.min = Math.min(avg.min, value);

                    if (avg.max == null)
                        avg.max = value;
                    else
                        avg.max = Math.max(avg.max, value);

                    avg.sum += value;
                    avg.num++;

                    if (key == "altitude") {
                        if (avg.incline == null)
                            avg.incline = 0;
                        if (avg.decline == null)
                            avg.decline = 0;

                        var altitude = datasets[key].data[l][1];
                        if (lastAltitude != null) {
                            if (altitude > lastAltitude) {
                                avg.incline += altitude - lastAltitude;
                            } else if (altitude < lastAltitude) {
                                avg.decline += lastAltitude - altitude;
                            }
                        }
                        lastAltitude = altitude;
                    }
                }
            }
            if (avg.num > 0) {
                avg.avg = avg.sum / avg.num;
                avg.label = datasets[key].label;
                avgs[key] = avg;
            }
        }
        return avgs;
    },
    plotAccordingToChoices: function(ranges, maxhr) {
        data = [];
        var min = null;
        var max = null;
        if (maxhr == undefined)
            maxhr = 0;
        var xaxisattrs = {
                tickDecimals: 0,
        };
        if (ranges.xaxis != undefined) {
            min = ranges.xaxis.from;
            max = ranges.xaxis.to;
            xaxisattrs.min = min;
            xaxisattrs.max = max;
        }
        $("#choices").find("input:checked").each(function () {
            var key = $(this).attr("name");
            if (key && datasets[key])
                data.push(datasets[key]);
        });

        if (data.length > 0) {
            plot = $.plot($("#tripdiv"), data, {
                yaxes: [{ min: 0 }, { min: 80 }],
                xaxis: xaxisattrs,
                legend: { container: $("#tripdiv_legend") },
                grid: {
                    hoverable: true,
                    clickable: true,
                },
                selection: { mode: "x" }
            });
        }

        avgs = this.findMinMaxAvg(datasets, min, max);
        var avglist = $("#averages ul").empty();

        // Only show if we've selected an area
        if (min != null) {
            var text = "";
            for (k in avgs) {
                text += "
" + (k != "altitude" ? avgs[k].label : "Ascent/Descent") + ": ";
                if (k != "altitude") {
                    text += Math.round(avgs[k].min*10)/10 + "/" + Math.round(avgs[k].max*10)/10 + "/" + Math.round(avgs[k].avg*10)/10;
                }
                else {
                    text += Math.round(avgs[k].incline*10)/10 + "/" + Math.round(avgs[k].decline*10)/10;
                }
                text += "
";
            }
            text += "
Distance: " + Math.round((avgs['speed'].end - avgs['speed'].start) * 10)/10 + "
";
            avglist.append(text);

            $("#averages h4").show();
        }
        else {
            $("#averages h4").hide();
        }

    },
    showTooltip: function (x, y, contents) {
        $('
' + contents + '
').css( {
            position: 'absolute',
            display: 'none',
            top: y + 5,
            left: x + 5,
            border: '1px solid #fdd',
            padding: '2px',
            'background-color': '#fee',
            opacity: 0.80
        }).appendTo("body").fadeIn(200);
    },
    init: function(datasets) {
        this.choiceContainer = $("#choices");
        var that = this;
        $.each(datasets, function(key, val) {
            that.choiceContainer.append('
<input type="checkbox" name="' + key +
                '" checked="checked" id="chk_' + key + '">' + val.label + '</input>');
        });
        this.choiceContainer.append('
<input type="reset" value="Reset zoom" />');
        this.choiceContainer.find("input").bind("click", function(evt) {
                that.plotAccordingToChoices({});
        });
        this.plotAccordingToChoices({});
        var previousPoint = null;
        $("#tripdiv").bind("plothover", function (event, pos, item) {
            var y = 0;
            if (pos.y)
                y = pos.y;
            else
                y = pos.y2;

            if (item) {
                if (previousPoint != item.datapoint) {
                    previousPoint = item.datapoint;

                    $("#tooltip").remove();
                    var x = item.datapoint[0].toFixed(2),
                        y = item.datapoint[1].toFixed(2);

                    that.showTooltip(item.pageX, item.pageY,
                    item.series.label + " at " + x + " is " + y);
                }
            }
            else {
                $("#tooltip").remove();
                previousPoint = null;
            }
        });

        $("#tripdiv").bind("plotclick", function (event, pos, item) {
            if (item) {
                $("#clickdata").text("You clicked point " + item.dataIndex + " in " + item.series.label + ".");
                plot.highlight(item.series, item.datapoint);
            }
        });

        $("#tripdiv").bind("plotselected", function (event, ranges) {
            that.plotAccordingToChoices(ranges);
        });
    }
};

<commit_msg>posisjonere aksene<commit_after>/**
 * Class for handeling graph plotting of pulse data.
 * Still needs loads of work and is only a cut/paste from template for now
 */
var plot;
var GraphPlotter = {
    choiceContainer: null,

    findMinMaxAvg: function (datasets, from, to) {
        var avgs = {};
        for (key in datasets) {
            var avg = { start: null, end: null, sum: 0, num: 0, avg: null, min: null, max: null, incline: null, decline: null, length: null };
            var lastAltitude = null;
            var label = datasets[key].label;
            for (l in datasets[key].data) {
                var pos = datasets[key].data[l][0];
                if (from != null && (pos >= from && pos <= to)) {
                    if (avg.start == null)
                        avg.start = pos;
                    avg.end = pos;

                    var value = datasets[key].data[l][1];
                    if (avg.min == null)
                        avg.min = value;
                    else
                        avg.min = Math.min(avg.min, value);

                    if (avg.max == null)
                        avg.max = value;
                    else
                        avg.max = Math.max(avg.max, value);

                    avg.sum += value;
                    avg.num++;

                    if (key == "altitude") {
                        if (avg.incline == null)
                            avg.incline = 0;
                        if (avg.decline == null)
                            avg.decline = 0;

                        var altitude = datasets[key].data[l][1];
                        if (lastAltitude != null) {
                            if (altitude > lastAltitude) {
                                avg.incline += altitude - lastAltitude;
                            } else if (altitude < lastAltitude) {
                                avg.decline += lastAltitude - altitude;
                            }
                        }
                        lastAltitude = altitude;
                    }
                }
            }
            if (avg.num > 0) {
                avg.avg = avg.sum / avg.num;
                avg.label = datasets[key].label;
                avgs[key] = avg;
            }
        }
        return avgs;
    },
    plotAccordingToChoices: function(ranges, maxhr) {
        data = [];
        var min = null;
        var max = null;
        if (maxhr == undefined)
            maxhr = 0;
        var xaxisattrs = {
                tickDecimals: 0,
        };
        if (ranges.xaxis != undefined) {
            min = ranges.xaxis.from;
            max = ranges.xaxis.to;
            xaxisattrs.min = min;
            xaxisattrs.max = max;
        }
        $("#choices").find("input:checked").each(function () {
            var key = $(this).attr("name");
            if (key && datasets[key])
                data.push(datasets[key]);
        });

        if (data.length > 0) {
            plot = $.plot($("#tripdiv"), data, {
                yaxes: [{ min: 0 }, { position: "right", min: 80 }, { position: "right"}],
                xaxis: xaxisattrs,
                legend: { container: $("#tripdiv_legend") },
                grid: {
                    hoverable: true,
                    clickable: true,
                },
                selection: { mode: "x" }
            });
        }

        avgs = this.findMinMaxAvg(datasets, min, max);
        var avglist = $("#averages ul").empty();

        // Only show if we've selected an area
        if (min != null) {
            var text = "";
            for (k in avgs) {
                text += "
" + (k != "altitude" ? avgs[k].label : "Ascent/Descent") + ": ";
                if (k != "altitude") {
                    text += Math.round(avgs[k].min*10)/10 + "/" + Math.round(avgs[k].max*10)/10 + "/" + Math.round(avgs[k].avg*10)/10;
                }
                else {
                    text += Math.round(avgs[k].incline*10)/10 + "/" + Math.round(avgs[k].decline*10)/10;
                }
                text += "
";
            }
            text += "
Distance: " + Math.round((avgs['speed'].end - avgs['speed'].start) * 10)/10 + "
";
            avglist.append(text);

            $("#averages h4").show();
        }
        else {
            $("#averages h4").hide();
        }

    },
    showTooltip: function (x, y, contents) {
        $('
' + contents + '
').css( {
            position: 'absolute',
            display: 'none',
            top: y + 5,
            left: x + 5,
            border: '1px solid #fdd',
            padding: '2px',
            'background-color': '#fee',
            opacity: 0.80
        }).appendTo("body").fadeIn(200);
    },
    init: function(datasets) {
        this.choiceContainer = $("#choices");
        var that = this;
        $.each(datasets, function(key, val) {
            that.choiceContainer.append('
<input type="checkbox" name="' + key +
                '" checked="checked" id="chk_' + key + '">' + val.label + '</input>');
        });
        this.choiceContainer.append('
<input type="reset" value="Reset zoom" />');
        this.choiceContainer.find("input").bind("click", function(evt) {
                that.plotAccordingToChoices({});
        });
        this.plotAccordingToChoices({});
        var previousPoint = null;
        $("#tripdiv").bind("plothover", function (event, pos, item) {
            var y = 0;
            if (pos.y)
                y = pos.y;
            else
                y = pos.y2;

            if (item) {
                if (previousPoint != item.datapoint) {
                    previousPoint = item.datapoint;

                    $("#tooltip").remove();
                    var x = item.datapoint[0].toFixed(2),
                        y = item.datapoint[1].toFixed(2);

                    that.showTooltip(item.pageX, item.pageY,
                    item.series.label + " at " + x + " is " + y);
                }
            }
            else {
                $("#tooltip").remove();
                previousPoint = null;
            }
        });

        $("#tripdiv").bind("plotclick", function (event, pos, item) {
            if (item) {
                $("#clickdata").text("You clicked point " + item.dataIndex + " in " + item.series.label + ".");
                plot.highlight(item.series, item.datapoint);
            }
        });

        $("#tripdiv").bind("plotselected", function (event, ranges) {
            that.plotAccordingToChoices(ranges);
        });
    }
};

<|endoftext|>


<commit_before>import moment from 'moment';
import { BaseScraperWithBrowser, LOGIN_RESULT } from './base-scraper-with-browser';
import { waitForRedirect } from '../helpers/navigation';
import {
  dropdownSelect,
  dropdownElements,
  fillInput,
  clickButton,
  waitUntilElementFound,
  elementPresentOnPage,
} from '../helpers/elements-interactions';

const BASE_URL = 'https://www.mizrahi-tefahot.co.il';
const LOGIN_URL = `${BASE_URL}/he/bank/Pages/Default.aspx`;
const AFTER_LOGIN_BASE_URL = 'https://mto.mizrahi-tefahot.co.il/ngOnline/index.html';
const DATE_FORMAT = 'DD/MM/YY';

const redirectedLoginPages = [
  'https://www.mizrahi-tefahot.co.il/he/bank/Pages/Default.aspx',
  'https://mto.mizrahi-tefahot.co.il/Online/Default.aspx?language=he-IL',
  'https://www.mizrahi-tefahot.co.il/login/MiddlePage.aspx',
];

function getTransactionsUrl() {
  return `${AFTER_LOGIN_BASE_URL}#/main/uis/osh/p428/`;
  // There is another url but I'm not sure it's different.
  // In addition, he made navigation problems so I left him.
  // return `${AFTER_LOGIN_BASE_URL}#/main/uis/legacy/Osh/p428//legacy.Osh.p428`;
}

async function fetchTransactionsForAccount(page, startDate, accountId) {
  await dropdownSelect(page, 'select#sky-account-combo', accountId);
  await waitUntilElementFound(page, 'a#btnCustomDate');
  await clickButton(page, 'a#btnCustomDate');
  await waitUntilElementFound(page, 'div#dvCustomDateRange');
  await fillInput(
    page,
    'input#ctl00_ContentPlaceHolder2_SkyDRP_SkyDatePicker1ID_radDatePickerID_dateInput',
    startDate.format(DATE_FORMAT),
  );
  await clickButton(page, 'input#btnShow');
  // TODO: continue from here
  await waitUntilElementFound(page, 'table#WorkSpaceBox table#ctlActivityTable');

  const hasExpandAllButton = await elementPresentOnPage(page, 'a#lnkCtlExpandAllInPage');

  if (hasExpandAllButton) {
    await clickButton(page, 'a#lnkCtlExpandAllInPage');
  }

  const selectedSnifAccount = await page.$eval('#ddlAccounts_m_ddl option[selected="selected"]', (option) => {
    return option.innerText;
  });

  const accountNumber = selectedSnifAccount.replace('/', '_');
  console.log(accountNumber);
  // const pendingTxns = await extractPendingTransactionsFromPage(page);
  // const completedTxns = await extractCompletedTransactionsFromPage(page);
  // const txns = [
  //   ...pendingTxns,
  //   ...completedTxns,
  // ];

  // return {
  //   accountNumber,
  //   txns: convertTransactions(txns),
  // };
}

async function fetchTransactions(page, startDate) {
  const res = [];
  // Loop through all available accounts and collect transactions from all
  const accounts = await dropdownElements(page, 'select#sky-account-combo');
  for (const account of accounts) {
    // Skip "All accounts" option
    if (account.value !== '-1') {
      res.push(await fetchTransactionsForAccount(page, startDate, account.value));
    }
  }
  return res;
}

function createLoginFields(credentials) {
  return [
    { selector: '#ctl00_PlaceHolderLogin_ctl00_tbUserName', value: credentials.username },
    { selector: '#ctl00_PlaceHolderLogin_ctl00_tbPassword', value: credentials.password },
  ];
}

function getPossibleLoginResults() {
  const urls = {};
  urls[LOGIN_RESULT.SUCCESS] = [`${AFTER_LOGIN_BASE_URL}`];
  urls[LOGIN_RESULT.INVALID_PASSWORD] = [`${BASE_URL}/login/loginMTO.aspx`];
  urls[LOGIN_RESULT.CHANGE_PASSWORD] = [
    `${AFTER_LOGIN_BASE_URL}/main/uis/ge/changePassword/`,
  ];
  return urls;
}

class MizrahiScraper extends BaseScraperWithBrowser {
  getLoginOptions(credentials) {
    return {
      loginUrl: `${LOGIN_URL}`,
      fields: createLoginFields(credentials),
      submitButtonSelector: '#ctl00_PlaceHolderLogin_ctl00_Enter',
      // TODO Replace waitForRedirect with waitUntilElementFound from leumi
      postAction: async () => waitForRedirect(this.page, undefined, undefined,
        redirectedLoginPages),
      possibleResults: getPossibleLoginResults(),
    };
  }

  async fetchData() {
    console.debug('Starting fetch Data - Mizrahi');
    const defaultStartMoment = moment().subtract(1, 'years').add(1, 'day');
    const startDate = this.options.startDate || defaultStartMoment.toDate();
    const startMoment = moment.max(defaultStartMoment, moment(startDate));

    const accounts = await fetchTransactions(this.page, startMoment);

    return {
      success: true,
      accounts,
    };
  }
}

export default MizrahiScraper;
<commit_msg>Replace navigateTo to WaitToElement in postLogin<commit_after>import moment from 'moment';
import { BaseScraperWithBrowser, LOGIN_RESULT } from './base-scraper-with-browser';
import { waitForRedirect } from '../helpers/navigation';
import {
  dropdownSelect,
  dropdownElements,
  fillInput,
  clickButton,
  waitUntilElementFound,
  elementPresentOnPage,
} from '../helpers/elements-interactions';

const BASE_URL = 'https://www.mizrahi-tefahot.co.il';
const LOGIN_URL = `${BASE_URL}/he/bank/Pages/Default.aspx`;
const AFTER_LOGIN_BASE_URL = 'https://mto.mizrahi-tefahot.co.il/ngOnline/index.html#/main/uis/osh/p428/';
const DATE_FORMAT = 'DD/MM/YY';

async function fetchTransactionsForAccount(page, startDate, accountId) {
  await dropdownSelect(page, 'select#sky-account-combo', accountId);
  await waitUntilElementFound(page, 'a#btnCustomDate');
  await clickButton(page, 'a#btnCustomDate');
  await waitUntilElementFound(page, 'div#dvCustomDateRange');
  await fillInput(
    page,
    'input#ctl00_ContentPlaceHolder2_SkyDRP_SkyDatePicker1ID_radDatePickerID_dateInput',
    startDate.format(DATE_FORMAT),
  );
  await clickButton(page, 'input#btnShow');
  // TODO: continue from here
  await waitUntilElementFound(page, 'table#WorkSpaceBox table#ctlActivityTable');

  const hasExpandAllButton = await elementPresentOnPage(page, 'a#lnkCtlExpandAllInPage');

  if (hasExpandAllButton) {
    await clickButton(page, 'a#lnkCtlExpandAllInPage');
  }

  const selectedSnifAccount = await page.$eval('#ddlAccounts_m_ddl option[selected="selected"]', (option) => {
    return option.innerText;
  });

  const accountNumber = selectedSnifAccount.replace('/', '_');
  console.log(accountNumber);
  // const pendingTxns = await extractPendingTransactionsFromPage(page);
  // const completedTxns = await extractCompletedTransactionsFromPage(page);
  // const txns = [
  //   ...pendingTxns,
  //   ...completedTxns,
  // ];

  // return {
  //   accountNumber,
  //   txns: convertTransactions(txns),
  // };
}

async function fetchTransactions(page, startDate) {
  const res = [];

  // Loop through all available accounts and collect transactions from all
  const accounts = await dropdownElements(page, 'select#sky-account-combo');
  for (const account of accounts) {
    // Skip "All accounts" option
    if (account.value !== '-1') {
      res.push(await fetchTransactionsForAccount(page, startDate, account.value));
    }
  }
  return res;
}

function createLoginFields(credentials) {
  return [
    { selector: '#ctl00_PlaceHolderLogin_ctl00_tbUserName', value: credentials.username },
    { selector: '#ctl00_PlaceHolderLogin_ctl00_tbPassword', value: credentials.password },
  ];
}

function getPossibleLoginResults() {
  const urls = {};
  urls[LOGIN_RESULT.SUCCESS] = [`${AFTER_LOGIN_BASE_URL}`];
  urls[LOGIN_RESULT.INVALID_PASSWORD] = [`${BASE_URL}/login/loginMTO.aspx`];
  urls[LOGIN_RESULT.CHANGE_PASSWORD] = [
    `${AFTER_LOGIN_BASE_URL}/main/uis/ge/changePassword/`,
  ];
  return urls;
}

async function waitForPostLogin(page) {
  return Promise.race([
    waitUntilElementFound(page, '#container', true),
  ]);
}

class MizrahiScraper extends BaseScraperWithBrowser {
  getLoginOptions(credentials) {
    return {
      loginUrl: `${LOGIN_URL}`,
      fields: createLoginFields(credentials),
      submitButtonSelector: '#ctl00_PlaceHolderLogin_ctl00_Enter',
      // TODO Replace waitForRedirect with waitUntilElementFound from leumi
      postAction: async () => waitForPostLogin(this.page),
      possibleResults: getPossibleLoginResults(),
    };
  }

  async fetchData() {
    console.debug('Starting fetch Data - Mizrahi');
    const defaultStartMoment = moment().subtract(1, 'years').add(1, 'day');
    const startDate = this.options.startDate || defaultStartMoment.toDate();
    const startMoment = moment.max(defaultStartMoment, moment(startDate));

    const accounts = await fetchTransactions(this.page, startMoment);

    return {
      success: true,
      accounts,
    };
  }
}

export default MizrahiScraper;
<|endoftext|>


<commit_before>module.exports = function(){
  'use strict';
  var d = document;
  //Beware; appendChild doesn't work in all cases in ie6.
  var attachPoint = d.getElementsByTagName('script')[0].parentNode;

  function script(url){
    var s = d.createElement('script');
    s.src = url;
    return attachPoint.appendChild(s);
  }

  function css(url){
    var l = d.createElement('link');
    l.ref = 'stylesheet';
    l.href = url;
    return attachPoint.appendChild(l);
  }

  return {
   css:css,
   script:script
  };

};
<commit_msg>callback for loading<commit_after>module.exports = function(){
  'use strict';
  var d = document;
  //Beware; appendChild doesn't work in all cases in ie6.
  var attachPoint = d.getElementsByTagName('script')[0].parentNode;

  function script(url, cb){
    var s = d.createElement('script');
    s.src = url;
    s.onload = cb;
    return attachPoint.appendChild(s);
  }

  function css(url, cb){
    var l = d.createElement('link');
    l.ref = 'stylesheet';
    l.href = url;
    //TODO img hack for css onload
    l.onload = cb;
    return attachPoint.appendChild(l);
  }

  return {
   css:css,
   script:script
  };

};
<|endoftext|>


<commit_before>'use strict';

export function generateEventHandlerForElement(aAttribute, aPropertyFactor, aParentWriteCallback) {
    return {
        mousedown: (aEvent) => {
            aEvent.preventDefault();
            let tmpMover = aEvent[aAttribute];
            let scrollPositionFloat = this._scrollerParent[aParentWriteCallback]();

            let tmpMovePointer = (e) => {
                scrollPositionFloat += (e[aAttribute] - tmpMover) * this[aPropertyFactor];
                tmpMover = e[aAttribute];

                this._scrollerParent[aParentWriteCallback](Math.round(scrollPositionFloat));
            }

            let tmpEndPointer = (e) => {
                document.body.removeEventListener('mousemove', tmpMovePointer);
                document.body.removeEventListener('mouseup', tmpEndPointer);
                document.body.removeEventListener('mouseleave', tmpEndPointer);

                tmpMovePointer = null;
                tmpEndPointer = null;
            }

            document.body.addEventListener('mousemove', tmpMovePointer);
            document.body.addEventListener('mouseup', tmpEndPointer);
            document.body.addEventListener('mouseleave', tmpEndPointer);
        },
        touchstart: (aEvent) => {
            aEvent.preventDefault();
            let touchToTrack = aEvent.which || 0;
            let tmpMover = aEvent.touches[touchToTrack][aAttribute];
            let scrollPositionFloat = this._scrollerParent[aParentWriteCallback]();

            let tmpMovePointer = (e) => {
                if (e.which !== touchToTrack) {
                    return;
                }

                scrollPositionFloat += (e.touches[touchToTrack][aAttribute] - tmpMover) * this[aPropertyFactor];
                tmpMover = e.touches[touchToTrack][aAttribute];

                this._scrollerParent[aParentWriteCallback](Math.round(scrollPositionFloat));
            }

            let tmpEndPointer = (e) => {
                if (e.which !== touchToTrack) {
                    return;
                }

                document.body.removeEventListener('touchmove', tmpMovePointer);
                document.body.removeEventListener('touchend', tmpEndPointer);
                document.body.removeEventListener('touchleave', tmpEndPointer);

                tmpMovePointer = null;
                tmpEndPointer = null;
            }

            document.body.addEventListener('touchmove', tmpMovePointer);
            document.body.addEventListener('touchend', tmpEndPointer);
            document.body.addEventListener('touchleave', tmpEndPointer);
        }
    }
}

export function applyOptionsToScollBarElement(aElement, aElementName, aOptions) {
    let stylesKey = aElementName + 'Styles';
    let classKey = aElementName + 'Class';

    if (aOptions[stylesKey] && toString.call(aOptions[stylesKey]) === '[object Object]') {
        Object.keys(aOptions[stylesKey]).forEach((aKey) => {
            aElement.style[aKey] = aOptions[stylesKey][aKey];
        });
    }

    if (aOptions[classKey] && toString.call(aOptions[classKey]) === '[object String]') {
        aElement.classList.add(aOptions[classKey]);
    }
    else if (Array.isArray(aOptions[classKey])) {
        aOptions[classKey].forEach((aClass) => {
            aElement.classList.add(aClass);
        });
    }
}<commit_msg>Corrected some oddities in internet explorer<commit_after>'use strict';

export function generateEventHandlerForElement(aAttribute, aPropertyFactor, aParentWriteCallback) {
    return {
        mousedown: (aEvent) => {
            aEvent.preventDefault();
            let tmpMover = aEvent[aAttribute];
            let scrollPositionFloat = this._scrollerParent[aParentWriteCallback]();

            let tmpMovePointer = (e) => {
                scrollPositionFloat += (e[aAttribute] - tmpMover) * this[aPropertyFactor];
                tmpMover = e[aAttribute];

                this._scrollerParent[aParentWriteCallback](Math.round(scrollPositionFloat));
            }

            let tmpEndPointer = (e) => {
                document.body.removeEventListener('mousemove', tmpMovePointer);
                document.body.removeEventListener('mouseup', tmpEndPointer);
                document.body.removeEventListener('mouseleave', tmpEndPointer);

                tmpMovePointer = null;
                tmpEndPointer = null;
            }

            document.body.addEventListener('mousemove', tmpMovePointer);
            document.body.addEventListener('mouseup', tmpEndPointer);
            document.body.addEventListener('mouseleave', tmpEndPointer);
        },
        touchstart: (aEvent) => {
            aEvent.preventDefault();
            let touchToTrack = aEvent.which || 0;
            let tmpMover = aEvent.touches[touchToTrack][aAttribute];
            let scrollPositionFloat = this._scrollerParent[aParentWriteCallback]();

            let tmpMovePointer = (e) => {
                if (e.which !== touchToTrack) {
                    return;
                }

                scrollPositionFloat += (e.touches[touchToTrack][aAttribute] - tmpMover) * this[aPropertyFactor];
                tmpMover = e.touches[touchToTrack][aAttribute];

                this._scrollerParent[aParentWriteCallback](Math.round(scrollPositionFloat));
            }

            let tmpEndPointer = (e) => {
                if (e.which !== touchToTrack) {
                    return;
                }

                document.body.removeEventListener('touchmove', tmpMovePointer);
                document.body.removeEventListener('touchend', tmpEndPointer);
                document.body.removeEventListener('touchleave', tmpEndPointer);

                tmpMovePointer = null;
                tmpEndPointer = null;
            }

            document.body.addEventListener('touchmove', tmpMovePointer);
            document.body.addEventListener('touchend', tmpEndPointer);
            document.body.addEventListener('touchleave', tmpEndPointer);
        }
    }
}

export function applyOptionsToScollBarElement(aElement, aElementName, aOptions) {
    let stylesKey = aElementName + 'Styles';
    let classKey = aElementName + 'Class';

    if (aOptions && aOptions[stylesKey] && typeof aOptions[stylesKey] === 'string' && !Array.isArray(aOptions[stylesKey])) {
        Object.keys(aOptions[stylesKey]).forEach((aKey) => {
            aElement.style[aKey] = aOptions[stylesKey][aKey];
        });
    }

    if (aOptions && aOptions[classKey] && aOptions[classKey] === 'string') {
        aElement.classList.add(aOptions[classKey]);
    }
    else if (aOptions && Array.isArray(aOptions[classKey])) {
        aOptions[classKey].forEach((aClass) => {
            aElement.classList.add(aClass);
        });
    }
}<|endoftext|>


<commit_before>
export const colors = {
  normal: '#aaa',
  secFonts: '#bbb',
  highlight: 'red',
  active: 'rgb(69, 121, 291)',
  unactive: 'grey',
}

export const size = {
  sm: 12,
  md: 14,
  ml: 17,
  lg: 20,
}
<commit_msg>Default theme style<commit_after>
export const colors = {
  normal: '#3c3c3c',
  secFonts: '#a0a0a0',
  highlight: 'red',
  active: 'rgb(69, 121, 291)',
  unactive: 'grey',
}

export const size = {
  sm: 12,
  md: 14,
  ml: 17,
  lg: 20,
  xlg: 24,
}
<|endoftext|>


<commit_before>export default {
    ENV: {
        APP_NAME: 'Example Name',           //Name of the site
        APP_URL: 'http://app.com/',         //The site's url
        API_URL: 'http://api.com/api',      //RR api
    },
    servers: [
        {
            name: 'First Demo',
            token_endpoint: 'http://api.app/api/oauth/access_token',
            auth_endpoint: 'http://api.app/api/oauth/authorize',
            client_id: 'secret',
            client_secret: 'secret'
        }
    ]
}<commit_msg>update example<commit_after>export default {
    ENV: {
        APP_NAME: 'Example Name',           //Name of the site
        APP_URL: 'http://app.com/',         //The site's url
        API_URL: 'http://api.com/api',      //RR api
    },
    servers: [
        {
            name: 'First Demo',
            token_endpoint: 'http://api.app/api/oauth/access_token',
            auth_endpoint: 'http://api.app/api/oauth/authorize',
            data_endpoint: 'http://api.app/api/v1',
            client_id: 'secret',
            client_secret: 'secret'
        }
    ]
}<|endoftext|>


<commit_before>const express = require('express');

const api = express();
const bodyParser = require('body-parser');

const User = require('./resources/user');
const Period = require('./resources/period');
const PeriodType = require('./resources/period_type');
const Timesheet = require('./resources/timesheet');

api.use(bodyParser.json()); // for parsing application/json
api.use(bodyParser.urlencoded({ extended: true })); // for parsing application/x-www-form-urlencoded

/**
 * add extra debug information to sentry
 */
api.use((req, res, next) => {
    const sentryClient = api.get('sentry_client');
    if (!sentryClient) {
        next();
        return;
    }

    // add request information
    sentryClient.mergeContext({
        extra: {
            url: req.originalUrl,
            method: req.method,
            params: req.params,
            request: req,
        },
    });

    next();
});

/**
 * add user information to the sentry client so we have this data to debug in case of errors
 */
api.param('user', (req, res, next, id) => {
    // try to get the user details from the User model and attach it to the request object
    User.get(api.get('pg'), id, (user) => {
        if (user) {
            const sentryClient = api.get('sentry_client');
            if (sentryClient) {
                sentryClient.mergeContext({ user });
            }
            next();
        } else {
            next(new Error('failed to load user'));
        }
    });
});

api.get('/users', (req, res) => {
    console.info('API GET Request for Users');
    User.list(api.get('pg'), (users) => {
        res.json(users);
    });
});

api.get('/period-types', (req, res) => {
    console.info('API GET Request for Period Types');
    PeriodType.list(api.get('pg'), (types) => {
        res.json(types);
    });
});

api.get('/users/:user/timesheet/:from/:to', (req, res) => {
    console.info('API GET Request for Timesheet', req.params.from, 'to', req.params.to, 'for user', req.params.user);
    Timesheet.get(api.get('pg'), req.params.user, req.params.from, req.params.to, (timesheet) => {
        res.json(timesheet);
    });
});

api.post('/users/:user/periods', (req, res) => {
    console.info('API POST Request for Period for user', req.params.user);
    const data = req.body;
    validateData(data, res);

    Period.post(api.get('pg'), req.params.user, data, (period) => {
        res.json(period);
    });
});

api.put('/users/:user/periods/:id', (req, res) => {
    console.info('API PUT Request for Period', req.params.id, 'for user', req.params.user);

    const data = req.body;
    if (parseInt(req.params.id, 10) !== data.per_id) {
        res.status(400).send('Invalid Id!').end();
    }

    validateData(data, res);

    Period.put(api.get('pg'), req.params.user, data, (period) => {
        res.json(period);
    });
});

api.delete('/users/:user/periods/:id', (req, res) => {
    console.info('API DELETE Request for Period');
    Period.delete(api.get('pg'), req.params.id, req.params.user, () => {
        res.status(204).end();
    });
});

api.all(/.*/, (req, res) => {
    res.status(400).send('Bad Request').end();
});

function validateData(data, res) {
    if (!data.userId || !data.per_pty_id) {
        res.status(400).send('Missing Data!').end();
        return false;
    }

    if (!data.per_start && !data.per_duration) {
        res.status(400).send('Missing Data \'Start\' or \'Duration\'!').end();
        return false;
    }

    return true;
}

exports.api = api;
<commit_msg>stop process if validation failed<commit_after>const express = require('express');

const api = express();
const bodyParser = require('body-parser');

const User = require('./resources/user');
const Period = require('./resources/period');
const PeriodType = require('./resources/period_type');
const Timesheet = require('./resources/timesheet');

api.use(bodyParser.json()); // for parsing application/json
api.use(bodyParser.urlencoded({ extended: true })); // for parsing application/x-www-form-urlencoded

/**
 * add extra debug information to sentry
 */
api.use((req, res, next) => {
    const sentryClient = api.get('sentry_client');
    if (!sentryClient) {
        next();
        return;
    }

    // add request information
    sentryClient.mergeContext({
        extra: {
            url: req.originalUrl,
            method: req.method,
            params: req.params,
            request: req,
        },
    });

    next();
});

/**
 * add user information to the sentry client so we have this data to debug in case of errors
 */
api.param('user', (req, res, next, id) => {
    // try to get the user details from the User model and attach it to the request object
    User.get(api.get('pg'), id, (user) => {
        if (user) {
            const sentryClient = api.get('sentry_client');
            if (sentryClient) {
                sentryClient.mergeContext({ user });
            }
            next();
        } else {
            next(new Error('failed to load user'));
        }
    });
});

api.get('/users', (req, res) => {
    console.info('API GET Request for Users');
    User.list(api.get('pg'), (users) => {
        res.json(users);
    });
});

api.get('/period-types', (req, res) => {
    console.info('API GET Request for Period Types');
    PeriodType.list(api.get('pg'), (types) => {
        res.json(types);
    });
});

api.get('/users/:user/timesheet/:from/:to', (req, res) => {
    console.info('API GET Request for Timesheet', req.params.from, 'to', req.params.to, 'for user', req.params.user);
    Timesheet.get(api.get('pg'), req.params.user, req.params.from, req.params.to, (timesheet) => {
        res.json(timesheet);
    });
});

api.post('/users/:user/periods', (req, res) => {
    console.info('API POST Request for Period for user', req.params.user);
    const data = req.body;
    if (!validateData(data, res)) {
        return;
    }

    Period.post(api.get('pg'), req.params.user, data, (period) => {
        res.json(period);
    });
});

api.put('/users/:user/periods/:id', (req, res) => {
    console.info('API PUT Request for Period', req.params.id, 'for user', req.params.user);

    const data = req.body;
    if (parseInt(req.params.id, 10) !== data.per_id) {
        res.status(400).send('Invalid Id!').end();
    }

    if (!validateData(data, res)) {
        return;
    }

    Period.put(api.get('pg'), req.params.user, data, (period) => {
        res.json(period);
    });
});

api.delete('/users/:user/periods/:id', (req, res) => {
    console.info('API DELETE Request for Period');
    Period.delete(api.get('pg'), req.params.id, req.params.user, () => {
        res.status(204).end();
    });
});

api.all(/.*/, (req, res) => {
    res.status(400).send('Bad Request').end();
});

function validateData(data, res) {
    if (!data.userId || !data.per_pty_id) {
        res.status(400).send('Missing Data!').end();
        return false;
    }

    if (!data.per_start && !data.per_duration) {
        res.status(400).send('Missing Data \'Start\' or \'Duration\'!').end();
        return false;
    }

    return true;
}

exports.api = api;
<|endoftext|>


<commit_before>import { AttrAssembler } from './AttrAssembler'
import { NodeAssembler } from './NodeAssembler'
import { ParentNodeAssembler } from './ParentNodeAssembler'

const { keys } = Object
const { isArray, prototype : { indexOf, map } } = Array
const { Element, Node : { ATTRIBUTE_NODE }, document } = window
const EMPTY_STRING = ''
const NAMESPACE_SEPARATOR = ':'
const NO_PARENT_INDEX = -1
const SELECT_ALL_SELECTOR = '*'
const SPACE = ' '
const registry = {}

let counter = Date.now()

/**
 * @see https://www.w3.org/TR/dom/#interface-element
 */
export class ElementAssembler extends ParentNodeAssembler {
    /**
     * @param {class|string} object - ElementAssembler or AttrAssembler subclass or selector
     * @param {function} [object.getInstanceOf]
     * @param {function} [object.getAttrOf]
     * @returns {ElementAssembler|AttrAssembler|*|null}
     */
    closest(object) {
        if(ElementAssembler.isPrototypeOf(object) || AttrAssembler.isPrototypeOf(object)) {
            const node = this.node.closest(object.selector)
            if(node) {
                const isAttr = AttrAssembler.isPrototypeOf(object)
                return isAttr?
                    object.getAttrOf(node) :
                    object.getInstanceOf(node)
            }
            return null
        }
        return this.getInstanceOf(this.node.closest(object))
    }

    /**
     * this.getAttribute(Foo)
     * this.getAttribute('foo')
     *
     * @param {class|string} attr
     * @param {function} [attr.getAttrOf]
     * @returns {string|*|null}
     */
    getAttr(attr) {
        if(AttrAssembler.isPrototypeOf(attr)) {
            const instance = attr.getAttrOf(this)
            return instance? instance.value : attr.defaultValue
        }
        return this.node.getAttribute(attr)
    }

    /**
     * this.hasAttr(Foo)
     * this.hasAttr('foo')
     *
     * @param {class|string} attr
     * @returns {boolean}
     */
    hasAttr(attr) {
        const node = this.node
        return AttrAssembler.isPrototypeOf(attr)?
            node.hasAttribute(attr.qualifiedName) :
            node.hasAttribute(attr)
    }

    /**
     * this.removeAttr(Foo.getAttrOf(this))
     * this.removeAttr(this.node.getAttribute('foo'))
     * this.removeAttr(Foo)
     * this.removeAttr('foo')
     *
     * @param {AttrAssembler|Attr|class|string} attr
     * @param {function} [attr.getAttrOf]
     */
    removeAttr(attr) {
        const node = this.node
        if(attr instanceof AttrAssembler) {
            attr.remove()
        }
        else if(AttrAssembler.isPrototypeOf(attr)) {
            const instance = attr.getAttrOf(this)
            instance && instance.remove()
        }
        else node.removeAttribute(attr.nodeType === ATTRIBUTE_NODE? attr.name : attr)
    }

    /**
     * this.setAttr(new Foo({ value : 'bar' }))
     * this.setAttr(document.createAttribute('foo'))
     * this.setAttr(Foo, 'bar')
     * this.setAttr('foo', 'bar')
     *
     * @param {AttrAssembler|Attr|class|string} attr
     * @param {function} [attr.getAttrOf]
     * @param {function} [attr.removeOnValue]
     * @param {string|*} value
     */
    setAttr(attr, value) {
        const node = this.node
        if(attr instanceof AttrAssembler) {
            attr.ownerElement = node
        }
        else if(attr.nodeType === ATTRIBUTE_NODE) {
            node.attributes.setNamedItem(attr)
        }
        else if(AttrAssembler.isPrototypeOf(attr)) {
            let instance = attr.getAttrOf(this)
            if(instance) {
                instance.value = value
            }
            else if(!attr.removeOnValue(value)) {
                instance = new attr({ value })
                instance.ownerElement = node
            }
        }
        else if(!this.constructor.attrAssembler.removeOnValue(value)) {
            node.setAttribute(attr, value)
        }
    }

    /**
     * Set content attributes on the element
     * @param {*} attributes
     */
    set attributes(attributes) {
        if(isArray(attributes)) {
            attributes.forEach(attr => this.setAttr(attr))
        }
        else if(attributes.constructor === Object) {
            for(let name in attributes) {
                const value = attributes[name]
                if(attributes.hasOwnProperty(name) && value !== null) {
                    this.setAttr(name, value)
                }
            }
        }
        else this.setAttr(attributes)
    }

    /**
     * Get all attributes of the element as an array
     * @returns {AttrAssembler[]}
     */
    get attributes() {
        return map.call(this.node.attributes, node => this.getInstanceOf(node))
    }

    /**
     * Set the class list of the element
     * @param {*} classList token / token list / token-boolean dict {string|string[]|{}}
     */
    set classList(classList) {
        if(classList) {
            const node = this.node
            if(isArray(classList)) {
                classList.forEach(token => node.classList.add(token))
            }
            else if(classList.constructor === Object) {
                keys(classList).forEach(token => {
                    node.classList.toggle(token, classList[token])
                })
            }
            else node.classList = classList
        }
    }

    /**
     * Get the class list of the element as an array
     * @returns {DOMTokenList} classList interface
     */
    get classList() {
        return this.node.classList
    }

    /**
     * Set the class name of the element
     * @param {string} className
     */
    set className(className) {
        this.node.className = className
    }

    /**
     * Get the class name of the element
     * @returns {string}
     */
    get className() {
        return this.node.className
    }

    /**
     * Get the element index among its sibling elements or -1 if it has no parent
     * @returns {number}
     */
    get elementIndex() {
        const parentNode = this.node.parentNode
        return parentNode?
            indexOf.call(parentNode.children, this.node) :
            NO_PARENT_INDEX
    }

    /**
     * Set the unique identifier on the element
     * @param {string} id
     */
    set id(id) {
        this.node.id = id
    }

    /**
     * Get the unique identifier of the element
     * @returns {string}
     */
    get id() {
        return this.node.id
    }

    /**
     * Create the specified Element node
     * @returns {Element|*} created element
     * @override
     */
    static create() {
        return document.createElementNS(this.namespace, this.qualifiedName)
    }

    /**
     * Generate a unique identifier among the document's tree
     * @returns {String}
     */
    static generateId() {
        let id
        do id = this.name + counter++
        while(document.getElementById(id))
        return id
    }

    /**
     * @param {string|Element|Document|DocumentFragment} [selectorOrContext=this.selector]
     * @param {Element|Document|DocumentFragment} [context=window.document]
     * @returns {ElementAssembler[]} array of initialized instances
     */
    static init(selectorOrContext = this.selector, context = document) {
        if(selectorOrContext.nodeType) {
            context = selectorOrContext
            selectorOrContext = this.selector
        }
        return map.call(context.querySelectorAll(selectorOrContext), node => {
            return new this({ node })
        })
    }

    /**
     * Register assembler by it's namespace and localName
     */
    static register() {
        const { namespace, localName } = this
        const name = namespace?
            localName? namespace + SPACE + localName : namespace :
            localName
        this.registry[name] = this
    }

    /**
     * @param {Element|*} node
     * @returns {ElementAssembler|*}
     * @override
     */
    static resolve(node) {
        const namespace = this.namespace || null
        const { namespaceURI, localName } = node
        if(namespace === namespaceURI && this.localName === localName) {
            return this
        }
        else {
            const registry = this.registry
            const assembler = namespaceURI?
                registry[namespaceURI + SPACE + localName] || registry[namespaceURI] :
                registry[localName]
            return assembler || ElementAssembler
        }
    }

    /**
     * @returns {{}}
     */
    static get registry() {
        return registry
    }

    /**
     * @returns {interface} Element
     * @override
     */
    static get interface() {
        return Element
    }

    /**
     * The local name of the element node
     * @returns {string}
     */
    static get localName() {
        return this === this.superAssembler?
            EMPTY_STRING :
            this.name
    }

    /**
     * Default namespace URI
     * @returns {string}
     */
    static get namespace() {
        return EMPTY_STRING
    }

    /**
     * The XML namespace prefix
     * @returns {string}
     */
    static get prefix() {
        return EMPTY_STRING
    }

    /**
     * The qualified name of the element node
     * @returns {string}
     */
    static get qualifiedName() {
        const { prefix, localName } = this
        return prefix?
            prefix + NAMESPACE_SEPARATOR + localName :
            localName
    }

    /**
     * @returns {string}
     */
    static get selector() {
        return this.localName || SELECT_ALL_SELECTOR
    }

    /**
     * @returns {class} ElementAssembler
     */
    static get superAssembler() {
        return ElementAssembler
    }
}

Object.defineProperty(NodeAssembler, 'ElementAssembler', { value : ElementAssembler })
<commit_msg>ElementAssembler: use duck typing instead of isPrototypeOf method<commit_after>import { AttrAssembler } from './AttrAssembler'
import { NodeAssembler } from './NodeAssembler'
import { ParentNodeAssembler } from './ParentNodeAssembler'

const { keys } = Object
const { isArray, prototype : { indexOf, map } } = Array
const { Element, Node : { ATTRIBUTE_NODE }, document } = window
const EMPTY_STRING = ''
const NAMESPACE_SEPARATOR = ':'
const NO_PARENT_INDEX = -1
const SELECT_ALL_SELECTOR = '*'
const SPACE = ' '
const registry = {}

let counter = Date.now()

/**
 * @see https://www.w3.org/TR/dom/#interface-element
 */
export class ElementAssembler extends ParentNodeAssembler {
    /**
     * @param {class|string} object - ElementAssembler or AttrAssembler subclass or selector
     * @param {function} [object.getInstanceOf]
     * @param {function} [object.getAttrOf]
     * @returns {ElementAssembler|AttrAssembler|*|null}
     */
    closest(object) {
        if('selector' in object) {
            const node = this.node.closest(object.selector)
            if(node) {
                if(typeof object.getAttrOf === 'function') {
                    return object.getAttrOf(node)
                }
                if(typeof object.getInstanceOf === 'function') {
                    return object.getInstanceOf(node)
                }
            }
            return null
        }
        return this.getInstanceOf(this.node.closest(object))
    }

    /**
     * this.getAttribute(Foo)
     * this.getAttribute('foo')
     *
     * @param {class|string} attr
     * @param {function} [attr.getAttrOf]
     * @returns {string|*|null}
     */
    getAttr(attr) {
        if(typeof attr.getAttrOf === 'function') {
            const instance = attr.getAttrOf(this)
            return instance? instance.value : attr.defaultValue
        }
        return this.node.getAttribute(attr)
    }

    /**
     * this.hasAttr(Foo)
     * this.hasAttr('foo')
     *
     * @param {class|string} attr
     * @returns {boolean}
     */
    hasAttr(attr) {
        const node = this.node
        return typeof attr === 'function' && 'qualifiedName' in attr?
            node.hasAttribute(attr.qualifiedName) :
            node.hasAttribute(attr)
    }

    /**
     * this.removeAttr(Foo.getAttrOf(this))
     * this.removeAttr(this.node.getAttribute('foo'))
     * this.removeAttr(Foo)
     * this.removeAttr('foo')
     *
     * @param {AttrAssembler|Attr|class|string} attr
     * @param {function} [attr.getAttrOf]
     */
    removeAttr(attr) {
        const node = this.node
        if(attr instanceof AttrAssembler) {
            attr.remove()
        }
        if(typeof attr.getAttrOf === 'function') {
            const instance = attr.getAttrOf(this)
            instance && instance.remove()
        }
        else node.removeAttribute(attr.nodeType === ATTRIBUTE_NODE? attr.name : attr)
    }

    /**
     * this.setAttr(new Foo({ value : 'bar' }))
     * this.setAttr(document.createAttribute('foo'))
     * this.setAttr(Foo, 'bar')
     * this.setAttr('foo', 'bar')
     *
     * @param {AttrAssembler|Attr|class|string} attr
     * @param {function} [attr.getAttrOf]
     * @param {function} [attr.removeOnValue]
     * @param {string|*} value
     */
    setAttr(attr, value) {
        const node = this.node
        if(attr instanceof AttrAssembler) {
            attr.ownerElement = node
        }
        else if(attr.nodeType === ATTRIBUTE_NODE) {
            node.attributes.setNamedItem(attr)
        }
        else if(typeof attr.getAttrOf === 'function') {
            let instance = attr.getAttrOf(this)
            if(instance) {
                instance.value = value
            }
            else if(!attr.removeOnValue(value)) {
                instance = new attr({ value })
                instance.ownerElement = node
            }
        }
        else if(!this.constructor.attrAssembler.removeOnValue(value)) {
            node.setAttribute(attr, value)
        }
    }

    /**
     * Set content attributes on the element
     * @param {*} attributes
     */
    set attributes(attributes) {
        if(isArray(attributes)) {
            attributes.forEach(attr => this.setAttr(attr))
        }
        else if(attributes.constructor === Object) {
            for(let name in attributes) {
                const value = attributes[name]
                if(attributes.hasOwnProperty(name) && value !== null) {
                    this.setAttr(name, value)
                }
            }
        }
        else this.setAttr(attributes)
    }

    /**
     * Get all attributes of the element as an array
     * @returns {AttrAssembler[]}
     */
    get attributes() {
        return map.call(this.node.attributes, node => this.getInstanceOf(node))
    }

    /**
     * Set the class list of the element
     * @param {*} classList token / token list / token-boolean dict {string|string[]|{}}
     */
    set classList(classList) {
        if(classList) {
            const node = this.node
            if(isArray(classList)) {
                classList.forEach(token => node.classList.add(token))
            }
            else if(classList.constructor === Object) {
                keys(classList).forEach(token => {
                    node.classList.toggle(token, classList[token])
                })
            }
            else node.classList = classList
        }
    }

    /**
     * Get the class list of the element as an array
     * @returns {DOMTokenList} classList interface
     */
    get classList() {
        return this.node.classList
    }

    /**
     * Set the class name of the element
     * @param {string} className
     */
    set className(className) {
        this.node.className = className
    }

    /**
     * Get the class name of the element
     * @returns {string}
     */
    get className() {
        return this.node.className
    }

    /**
     * Get the element index among its sibling elements or -1 if it has no parent
     * @returns {number}
     */
    get elementIndex() {
        const parentNode = this.node.parentNode
        return parentNode?
            indexOf.call(parentNode.children, this.node) :
            NO_PARENT_INDEX
    }

    /**
     * Set the unique identifier on the element
     * @param {string} id
     */
    set id(id) {
        this.node.id = id
    }

    /**
     * Get the unique identifier of the element
     * @returns {string}
     */
    get id() {
        return this.node.id
    }

    /**
     * Create the specified Element node
     * @returns {Element|*} created element
     * @override
     */
    static create() {
        return document.createElementNS(this.namespace, this.qualifiedName)
    }

    /**
     * Generate a unique identifier among the document's tree
     * @returns {String}
     */
    static generateId() {
        let id
        do id = this.name + counter++
        while(document.getElementById(id))
        return id
    }

    /**
     * @param {string|Element|Document|DocumentFragment} [selectorOrContext=this.selector]
     * @param {Element|Document|DocumentFragment} [context=window.document]
     * @returns {ElementAssembler[]} array of initialized instances
     */
    static init(selectorOrContext = this.selector, context = document) {
        if(selectorOrContext.nodeType) {
            context = selectorOrContext
            selectorOrContext = this.selector
        }
        return map.call(context.querySelectorAll(selectorOrContext), node => {
            return new this({ node })
        })
    }

    /**
     * Register assembler by it's namespace and localName
     */
    static register() {
        const { namespace, localName } = this
        const name = namespace?
            localName? namespace + SPACE + localName : namespace :
            localName
        this.registry[name] = this
    }

    /**
     * @param {Element|*} node
     * @returns {ElementAssembler|*}
     * @override
     */
    static resolve(node) {
        const namespace = this.namespace || null
        const { namespaceURI, localName } = node
        if(namespace === namespaceURI && this.localName === localName) {
            return this
        }
        else {
            const registry = this.registry
            const assembler = namespaceURI?
                registry[namespaceURI + SPACE + localName] || registry[namespaceURI] :
                registry[localName]
            return assembler || ElementAssembler
        }
    }

    /**
     * @returns {{}}
     */
    static get registry() {
        return registry
    }

    /**
     * @returns {interface} Element
     * @override
     */
    static get interface() {
        return Element
    }

    /**
     * The local name of the element node
     * @returns {string}
     */
    static get localName() {
        return this === this.superAssembler?
            EMPTY_STRING :
            this.name
    }

    /**
     * Default namespace URI
     * @returns {string}
     */
    static get namespace() {
        return EMPTY_STRING
    }

    /**
     * The XML namespace prefix
     * @returns {string}
     */
    static get prefix() {
        return EMPTY_STRING
    }

    /**
     * The qualified name of the element node
     * @returns {string}
     */
    static get qualifiedName() {
        const { prefix, localName } = this
        return prefix?
            prefix + NAMESPACE_SEPARATOR + localName :
            localName
    }

    /**
     * @returns {string}
     */
    static get selector() {
        return this.localName || SELECT_ALL_SELECTOR
    }

    /**
     * @returns {class} ElementAssembler
     */
    static get superAssembler() {
        return ElementAssembler
    }
}

Object.defineProperty(NodeAssembler, 'ElementAssembler', { value : ElementAssembler })
<|endoftext|>


<commit_before>/**
 * @file localStorage
 * @author schoeu1110@gmail.com
 * @module tools/localStorage
 */

define(function (require) {
    'use strict';

    var $ = require('zepto');

    var localStorage = window.localStorage;
    var ls = {};

    /**
     * localStorage get操作
     * @type {string} key  标志
     * @return {string}    值
     * */
    ls.get = function (key) {
        key = key || '';
        return localStorage.getItem(key);
    };

    ls.set = function (key, value) {
        if (arguments.length > 1) {
            var temp;
            if ($.isPlainObject(value)) {
                try {
                    temp = JSON.parse(value);
                }
                catch (e) {
                    temp = '';
                }
            }
            else {
                temp = value;
            }
            localStorage.setItem(key, temp);
        }
    };

    ls.remove = function (key) {
        localStorage.removeItem(key);
    };

    ls.clear = function () {
        localStorage.clear();
    };

    return ls;
});
<commit_msg>update: localStorage.js<commit_after>/**
 * @file localStorage
 * @author schoeu1110@gmail.com
 * @module tools/localStorage
 */

define(function (require) {
    'use strict';

    var $ = require('zepto');

    var localStorage = window.localStorage;
    var ls = {};

    /**
     * localStorage get操作
     * @param {string} key  标志
     * @return {string}    值
     * */
    ls.get = function (key) {
        key = key || '';
        var value = localStorage.getItem(key);
        if (value && $.isPlainObject(value)) {
            try {
                value = JSON.parse(value);
            }
            catch (e) {
                // catch
            }
        }
        return value;
    };

    /**
     * localStorage set操作
     * @param {string} key  标志
     * @param {string|object} value  值
     * */
    ls.set = function (key, value, time) {
        var me = this;
        if (arguments.length > 1) {
            if ($.isPlainObject(value)) {
                try {
                    value = JSON.stringify(value);
                }
                catch (e) {
                }
            }
            localStorage.setItem(key, value);
        }
        setTimeout(function () {
            me.remove(key);
        }, time);
    };

    /**
     * localStorage 移除操作
     * @param {string} key  需要移除的key值
     * */
    ls.remove = function (key) {
        localStorage.removeItem(key);
    };

    /**
     * localStorage 清除操作
     * */
    ls.clear = function () {
        localStorage.clear();
    };

    /**
     * localStorage 获取localStorage长度
     * */
    ls.length = function () {
        return localStorage.length;
    };

    return ls;
});
<|endoftext|>


<commit_before>// -------------------------------------------------------------------------- \\
// File: RootView.js                                                          \\
// Module: View                                                               \\
// Requires: View.js                                                          \\
// Author: Neil Jenkins                                                       \\
// License: © 2010–2013 Opera Software ASA. All rights reserved.              \\
// -------------------------------------------------------------------------- \\

"use strict";

( function ( NS ) {

/*global window */

/**
    Class: O.RootView

    Extends: O.View

    An O.RootView instance uses an existing DOM node for its layer, and forms
    the root of the O.View tree making up your application. The root view adds
    DOM event listeners to its layer to observe and dispatch events for the
    whole view hierarchy.

        MyApp.views.mainWindow = new O.RootView( document );

    Normally, you will create an O.RootView instance with the document node for
    each window in your application, but if your application is not taking over
    the full page, it can be initiated with any other node already in the
    document.
*/
var RootView = NS.Class({

    Extends: NS.View,

    syncOnlyInDocument: false,

    layer: null,

    init: function ( node, mixin ) {
        RootView.parent.init.call( this, mixin );

        // Node.DOCUMENT_NODE => 9.
        var nodeIsDocument = ( node.nodeType === 9 ),
            doc = nodeIsDocument ? node : node.ownerDocument,
            win = doc.defaultView,
            events, l;

        events = [
            'click', 'mousedown', 'mouseup',
            'keypress', 'keydown', 'keyup',
            'dragstart', 'selectstart',
            'touchstart', 'touchmove', 'touchend', 'touchcancel',
            'cut'
        ];
        for ( l = events.length; l--; ) {
            node.addEventListener( events[l], this, false );
        }
        // These events don't bubble: have to capture.
        // In IE, we use a version of focus and blur which will bubble, but
        // there's no way of bubbling/capturing change and input.
        // These events are automatically added to all inputs when created
        // instead.
        events = [ 'focus', 'blur', 'change', 'input' ];
        for ( l = events.length; l--; ) {
            node.addEventListener( events[l], this, true );
        }
        events = [ 'resize', 'orientationchange', 'scroll' ];
        for ( l = events.length; l--; ) {
            win.addEventListener( events[l], this, false );
        }

        this.isRendered = true;
        this.isInDocument = true;
        this.layer = nodeIsDocument ? node.body : node;
    },

    _onScroll: function ( event ) {
        var layer = this.get( 'layer' ),
            isBody = ( layer.nodeName === 'BODY' ),
            doc = layer.ownerDocument,
            win = doc.defaultView,
            html = doc.documentElement,
            left = isBody ?
                // pageXOffset for everything but IE8.
                win.pageXOffset || html.scrollLeft || 0 :
                layer.scrollLeft,
            top = isBody ?
                // pageYOffset for everything but IE8.
                win.pageYOffset || html.scrollTop || 0 :
                layer.scrollTop;
        this.beginPropertyChanges()
                .set( 'scrollLeft', left )
                .set( 'scrollTop', top )
            .endPropertyChanges();
        event.stopPropagation();
    }.on( 'scroll' ),

    hideAddressBar: function () {
        window.scrollTo( 0, 0 );
    },

    pxTop: 0,
    pxLeft: 0,

    handleEvent: function ( event ) {
        switch ( event.type ) {
        // We observe mousemove when mousedown.
        case 'mousedown':
            this.get( 'layer' ).ownerDocument
                .addEventListener( 'mousemove', this, false );
            break;
        case 'mouseup':
            this.get( 'layer' ).ownerDocument
                .removeEventListener( 'mousemove', this, false );
            break;
        // Window resize events: just notify parent has resized.
        case 'orientationchange':
            this.hideAddressBar();
            /* falls through */
        case 'resize':
            this.parentViewDidResize();
            return;
        // Scroll events are special.
        case 'scroll':
            this._onScroll( event );
            return;
        }
        NS.ViewEventsController.handleEvent( event );
    }.invokeInRunLoop()
});

NS.RootView = RootView;

}( this.O ) );
<commit_msg>Simplify RootView resize event handling.<commit_after>// -------------------------------------------------------------------------- \\
// File: RootView.js                                                          \\
// Module: View                                                               \\
// Requires: View.js                                                          \\
// Author: Neil Jenkins                                                       \\
// License: © 2010–2013 Opera Software ASA. All rights reserved.              \\
// -------------------------------------------------------------------------- \\

"use strict";

( function ( NS ) {

/*global window */

/**
    Class: O.RootView

    Extends: O.View

    An O.RootView instance uses an existing DOM node for its layer, and forms
    the root of the O.View tree making up your application. The root view adds
    DOM event listeners to its layer to observe and dispatch events for the
    whole view hierarchy.

        MyApp.views.mainWindow = new O.RootView( document );

    Normally, you will create an O.RootView instance with the document node for
    each window in your application, but if your application is not taking over
    the full page, it can be initiated with any other node already in the
    document.
*/
var RootView = NS.Class({

    Extends: NS.View,

    syncOnlyInDocument: false,

    layer: null,

    init: function ( node, mixin ) {
        RootView.parent.init.call( this, mixin );

        // Node.DOCUMENT_NODE => 9.
        var nodeIsDocument = ( node.nodeType === 9 ),
            doc = nodeIsDocument ? node : node.ownerDocument,
            win = doc.defaultView,
            events, l;

        events = [
            'click', 'mousedown', 'mouseup',
            'keypress', 'keydown', 'keyup',
            'dragstart', 'selectstart',
            'touchstart', 'touchmove', 'touchend', 'touchcancel',
            'cut'
        ];
        for ( l = events.length; l--; ) {
            node.addEventListener( events[l], this, false );
        }
        // These events don't bubble: have to capture.
        // In IE, we use a version of focus and blur which will bubble, but
        // there's no way of bubbling/capturing change and input.
        // These events are automatically added to all inputs when created
        // instead.
        events = [ 'focus', 'blur', 'change', 'input' ];
        for ( l = events.length; l--; ) {
            node.addEventListener( events[l], this, true );
        }
        events = [ 'resize', 'orientationchange', 'scroll' ];
        for ( l = events.length; l--; ) {
            win.addEventListener( events[l], this, false );
        }

        this.isRendered = true;
        this.isInDocument = true;
        this.layer = nodeIsDocument ? node.body : node;
    },

    _onScroll: function ( event ) {
        var layer = this.get( 'layer' ),
            isBody = ( layer.nodeName === 'BODY' ),
            doc = layer.ownerDocument,
            win = doc.defaultView,
            html = doc.documentElement,
            left = isBody ?
                // pageXOffset for everything but IE8.
                win.pageXOffset || html.scrollLeft || 0 :
                layer.scrollLeft,
            top = isBody ?
                // pageYOffset for everything but IE8.
                win.pageYOffset || html.scrollTop || 0 :
                layer.scrollTop;
        this.beginPropertyChanges()
                .set( 'scrollLeft', left )
                .set( 'scrollTop', top )
            .endPropertyChanges();
        event.stopPropagation();
    }.on( 'scroll' ),

    hideAddressBar: function () {
        window.scrollTo( 0, 0 );
    },

    pxTop: 0,
    pxLeft: 0,

    handleEvent: function ( event ) {
        switch ( event.type ) {
        // We observe mousemove when mousedown.
        case 'mousedown':
            this.get( 'layer' ).ownerDocument
                .addEventListener( 'mousemove', this, false );
            break;
        case 'mouseup':
            this.get( 'layer' ).ownerDocument
                .removeEventListener( 'mousemove', this, false );
            break;
        // Window resize events: just notify parent has resized.
        case 'orientationchange':
            this.hideAddressBar();
            /* falls through */
        case 'resize':
            this.didResize();
            return;
        // Scroll events are special.
        case 'scroll':
            this._onScroll( event );
            return;
        }
        NS.ViewEventsController.handleEvent( event );
    }.invokeInRunLoop()
});

NS.RootView = RootView;

}( this.O ) );
<|endoftext|>


<commit_before>window.requestAnimFrame = (function(){
    return  window.requestAnimationFrame       ||
            window.webkitRequestAnimationFrame ||
            window.mozRequestAnimationFrame    ||
            function( callback ){
                window.setTimeout(callback, 1000 / 60);
            };
})();

Array.Generate = function(val, size) {
    var x = [];
    while(x.length < size)
        x.push(val);
    return x;
};

var Mouse = {
    x: 0,
    y: 0,

    buttons : {
        left: false,
        right: false
    },

    getRelativeTo: function(element) {
        var rect = element.getBoundingClientRect();
        return {
            x: Mouse.x - rect.left,
            y: Mouse.y - rect.top
        };
    },

    init: function() {
        document.addEventListener('mousemove', function(evt){
            Mouse.x = evt.clientX;
            Mouse.y = evt.clientY;
        }, false);

        document.body.addEventListener('mousedown', function (e){
            if(e.button === 0 || e.button === 1)
                Mouse.buttons.left = true;
            else if (e.button === 2)
                Mouse.buttons.right = true;
        }, false);

        document.body.addEventListener('mouseup', function (e){
            if(e.button === 0 || e.button === 1)
                Mouse.buttons.left = false;
            else if (e.button === 2)
                Mouse.buttons.right = false;
        }, false);
    }
};

function FField(canvas, debug) {
    this.debug = debug || false;

    this.gridResolution = 128;
    this.gridResPlus2 = this.gridResolution + 2;
    this.bufferSize = (this.gridResolution + 2) * (this.gridResolution + 2);
    this.dt = 0.1;
    this.diffusionRate = 0.0;
    this.viscocity = 0.0;
    this.force = 5.0; // scales the mouse movement that generate a force
    this.source = 300.0; // amount of density that will be deposited
    this.mouse = {x: 0, y: 0};

    this.u = Array.Generate(0, this.bufferSize);
    this.v = Array.Generate(0, this.bufferSize);
    this.dens = Array.Generate(0, this.bufferSize);

    this.u_prev = Array.Generate(0, this.bufferSize);
    this.v_prev = Array.Generate(0, this.bufferSize);
    this.dens_prev = Array.Generate(0, this.bufferSize);

    this.fpsStack = Array.Generate(this.dt, 100);

    this.canvas = canvas;
    this.context = this.canvas.getContext('2d');

    var self = this;
    window.requestAnimFrame(function() {self.tick();});
}

FField.prototype = {
    tick: function(_dt) {
        var now = new Date().getTime();
        this.dt = typeof _dt !== "undefined" ? _dt : 0.1;
        this.fpsStack.shift();
        this.fpsStack.push(1 / this.dt);

        this.update();
        this.draw();

        if(this.debug) {
            var fps = this.fpsStack.reduce(function(x, y){return x + y;}, 0) / this.fpsStack.length;
            fps = Math.round(fps * 10) / 10;

            this.context.font = '18pt Arial';
            this.context.fillStyle = 'black';
            this.context.fillText("FPS: " + fps, 10, 25);

            this.context.fillText("Mouse: X: " + this.mouse.x + "  Y: " + this.mouse.y, 10 , 50);
        }

        var self = this;
        window.requestAnimFrame(function() {
            self.tick((new Date().getTime() - now) / 1000)
        });
    },

    update: function () {
        this.u_prev = Array.Generate(0, this.bufferSize);
        this.v_prev = Array.Generate(0, this.bufferSize);
        this.dens_prev = Array.Generate(0, this.bufferSize);

        this.handleInput();
        this.velocityStep();
        this.densityStep();
    },

    handleInput: function() {
        this.mouse = Mouse.getRelativeTo(this.canvas);

        // Lower boundary
        this.mouse.x = Math.max(0, this.mouse.x);
        this.mouse.y = Math.max(0, this.mouse.y);

        // Upper boundary
        this.mouse.x = Math.min(this.canvas.width, this.mouse.x);
        this.mouse.y = Math.min(this.canvas.height, this.mouse.y);

        var gridX = ((this.mouse.x / 512) * this.gridResolution + 1);
        var gridY = (((512 - this.mouse.y) / 512) * this.gridResolution + 1);

        if(gridX < 1 || gridX > this.gridResolution || gridY < 1 || gridY > this.gridResolution)
            return;

        if(Mouse.buttons.left) {
            //TODO
        }

        if(Mouse.buttons.right) {
            //TODO
        }
    },

    velocityStep: function(){

    },

    densityStep: function() {

    },

    draw: function() {
        this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);

    }
};<commit_msg>Input logic<commit_after>window.requestAnimFrame = (function(){
    return  window.requestAnimationFrame       ||
            window.webkitRequestAnimationFrame ||
            window.mozRequestAnimationFrame    ||
            function( callback ){
                window.setTimeout(callback, 1000 / 60);
            };
})();

Array.Generate = function(val, size) {
    var x = [];
    while(x.length < size)
        x.push(val);
    return x;
};

var Mouse = {
    x: 0,
    y: 0,

    buttons : {
        left: false,
        right: false
    },

    getRelativeTo: function(element) {
        var rect = element.getBoundingClientRect();
        return {
            x: Mouse.x - rect.left,
            y: Mouse.y - rect.top
        };
    },

    init: function() {
        document.addEventListener('mousemove', function(evt){
            Mouse.x = evt.clientX;
            Mouse.y = evt.clientY;
        }, false);

        document.body.addEventListener('mousedown', function (e){
            if(e.button === 0 || e.button === 1)
                Mouse.buttons.left = true;
            else if (e.button === 2)
                Mouse.buttons.right = true;
        }, false);

        document.body.addEventListener('mouseup', function (e){
            if(e.button === 0 || e.button === 1)
                Mouse.buttons.left = false;
            else if (e.button === 2)
                Mouse.buttons.right = false;
        }, false);
    }
};

function FField(canvas, debug) {
    this.debug = debug || false;

    this.gridResolution = 128;
    this.gridResPlus2 = this.gridResolution + 2;
    this.bufferSize = (this.gridResolution + 2) * (this.gridResolution + 2);
    this.dt = 0.1;
    this.diffusionRate = 0.0;
    this.viscocity = 0.0;
    this.force = 5.0; // scales the mouse movement that generate a force
    this.source = 300.0; // amount of density that will be deposited
    this.mouse = {x: 0, y: 0};

    this.u = Array.Generate(0, this.bufferSize);
    this.v = Array.Generate(0, this.bufferSize);
    this.dens = Array.Generate(0, this.bufferSize);

    this.u_prev = Array.Generate(0, this.bufferSize);
    this.v_prev = Array.Generate(0, this.bufferSize);
    this.dens_prev = Array.Generate(0, this.bufferSize);

    this.fpsStack = Array.Generate(this.dt, 100);

    this.canvas = canvas;
    this.context = this.canvas.getContext('2d');

    var self = this;
    window.requestAnimFrame(function() {self.tick();});
}

FField.prototype = {
    tick: function(_dt) {
        var now = new Date().getTime();
        this.dt = typeof _dt !== "undefined" ? _dt : 0.1;
        this.fpsStack.shift();
        this.fpsStack.push(1 / this.dt);

        this.update();
        this.draw();

        if(this.debug) {
            var fps = this.fpsStack.reduce(function(x, y){return x + y;}, 0) / this.fpsStack.length;
            fps = Math.round(fps * 10) / 10;

            this.context.font = '18pt Arial';
            this.context.fillStyle = 'black';
            this.context.fillText("FPS: " + fps, 10, 25);

            this.context.fillText("Mouse: X: " + this.mouse.x + "  Y: " + this.mouse.y, 10 , 50);
        }

        var self = this;
        window.requestAnimFrame(function() {
            self.tick((new Date().getTime() - now) / 1000)
        });
    },

    update: function () {
        this.u_prev = Array.Generate(0, this.bufferSize);
        this.v_prev = Array.Generate(0, this.bufferSize);
        this.dens_prev = Array.Generate(0, this.bufferSize);

        this.handleInput();
        this.velocityStep();
        this.densityStep();
    },

    handleInput: function() {
        var cMouse = {x: 0, y: 0};

        cMouse = Mouse.getRelativeTo(this.canvas);

        // Lower boundary
        cMouse.x = Math.max(0, this.mouse.x);
        cMouse.y = Math.max(0, this.mouse.y);

        // Upper boundary
        cMouse.x = Math.min(this.canvas.width, this.mouse.x);
        cMouse.y = Math.min(this.canvas.height, this.mouse.y);

        if(this.mouse.x == 0)
            this.mouse = cMouse;

        var gridX = ((this.mouse.x / 512) * this.gridResolution + 1);
        var gridY = (((512 - this.mouse.y) / 512) * this.gridResolution + 1);

        if(gridX < 1 || gridX > this.gridResolution || gridY < 1 || gridY > this.gridResolution)
            return;

        if(Mouse.buttons.left) {
            this.u[this.IX(gridX, gridY)] = this.force * (cMouse.x - this.mouse.x);
            this.v[this.IX(gridX, gridY)] = this.force * (this.mouse.y - cMouse.y);
        }

        if(Mouse.buttons.right) {
            this.dens_prev[this.IX(gridX, gridY)] = this.source;
        }

        this.mouse = cMouse;
    },

    velocityStep: function(){

    },

    densityStep: function() {

    },

    draw: function() {
        this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);

    },

    IX: function(x, y) {
        return this.gridResPlus2 * y + x;
    }
};<|endoftext|>


<commit_before>var { h, computed, when } = require('mutant')
var nest = require('depnest')

exports.needs = nest({
  'keys.sync.id': 'first',
  'message.obs.likes': 'first',
  'sbot.async.publish': 'first'
})

exports.gives = nest('message.html.action')

exports.create = (api) => {
  return nest('message.html.action', function like (msg) {
    var id = api.keys.sync.id()
    var liked = computed([api.message.obs.likes(msg.key), id], doesLike)
    return when(liked,
      h('a.unlike', {
        href: '#',
        'ev-click': () => publishLike(msg, false)
      }, 'Unlike'),
      h('a.like', {
        href: '#',
        'ev-click': () => publishLike(msg, true)
      }, 'Like')
    )
  })

  function publishLike (msg, status = true) {
    var dig = status ? {
      type: 'vote',
      channel: msg.value.content.channel,
      vote: { link: msg.key, value: 1, expression: 'Dig' }
    } : {
      type: 'vote',
      channel: msg.value.content.channel,
      vote: { link: msg.key, value: 0, expression: 'Undig' }
    }
    if (msg.value.content.recps) {
      dig.recps = msg.value.content.recps.map(function (e) {
        return e && typeof e !== 'string' ? e.link : e
      })
      dig.private = true
    }
    api.sbot.async.publish(dig)
  }
}

function doesLike (likes, userId) {
  return likes.includes(userId)
}
<commit_msg>change like expression to "Like"<commit_after>var { h, computed, when } = require('mutant')
var nest = require('depnest')

exports.needs = nest({
  'keys.sync.id': 'first',
  'message.obs.likes': 'first',
  'sbot.async.publish': 'first'
})

exports.gives = nest('message.html.action')

exports.create = (api) => {
  return nest('message.html.action', function like (msg) {
    var id = api.keys.sync.id()
    var liked = computed([api.message.obs.likes(msg.key), id], doesLike)
    return when(liked,
      h('a.unlike', {
        href: '#',
        'ev-click': () => publishLike(msg, false)
      }, 'Unlike'),
      h('a.like', {
        href: '#',
        'ev-click': () => publishLike(msg, true)
      }, 'Like')
    )
  })

  function publishLike (msg, status = true) {
    var like = status ? {
      type: 'vote',
      channel: msg.value.content.channel,
      vote: { link: msg.key, value: 1, expression: 'Like' }
    } : {
      type: 'vote',
      channel: msg.value.content.channel,
      vote: { link: msg.key, value: 0, expression: 'Unlike' }
    }
    if (msg.value.content.recps) {
      like.recps = msg.value.content.recps.map(function (e) {
        return e && typeof e !== 'string' ? e.link : e
      })
      like.private = true
    }
    api.sbot.async.publish(like)
  }
}

function doesLike (likes, userId) {
  return likes.includes(userId)
}
<|endoftext|>


<commit_before>/**
 * Provides a cache outside of Redux state that can optimistically update state
 * before an asynchronous API call returns
 *
 * @module CacheMiddleware
 */
import Rx from 'rxjs';
import { combineEpics } from 'redux-observable';
import {
	cacheSuccess,
} from '../actions/cacheActionCreators';

import {
	makeCache,
	cacheReader,
	cacheWriter,
} from '../util/cacheUtils';

export function checkEnable() {
	if (typeof window !== 'undefined' && window.location) {
		const params = new URLSearchParams(window.location.search.slice(1));
		return !params.has('__nocache');
	}
	return true;
}

/**
 * The cache middleware triggers a 'set'/store action when new data is received
 * from the API (API_SUCCESS), and is queried when queries are sent to the API
 * (API_REQUEST). These events trigger cache-specific events, CACHE_SET and
 * CACHE_QUERY, which are then used to update the cache or update the
 * application state (CACHE_SUCCESS)
 *
 * @returns {Function} the curried state => action => next middleware function
 */

// get a cache, any cache (that conforms to the Promise-based API)
const cache = makeCache();

// get a function that can read from the cache for a specific query
const readCache = cacheReader(cache);
// get a function that can write to the cache for a specific query-response
const writeCache = cacheWriter(cache);

/**
 * Listen for any action that should clear cached state
 *
 * Note that this will clear the cache without emitting an action
 */
export const cacheClearEpic = action$ =>
	action$.ofType('LOGOUT_REQUEST', 'CACHE_CLEAR')
		.flatMap(() => cache.clear())  // wait for cache to clear before continuing
		.flatMap(() => Rx.Observable.empty());

/**
 * Listen for any action that should set cached state with a
 * `{ queries, responses }` payload
 *
 * API_SUCCESS means there is fresh data ready to be stored - extract the
 * queries and their responses, then dispatch `CACHE_SET` actions with each
 * pair
 *
 * Not that this will set the cache without emitting an action
 */
export const cacheSetEpic = action$ =>
	action$.ofType('API_SUCCESS', 'CACHE_SET')
		.flatMap(({ payload: { queries, responses } }) =>
			Rx.Observable.from(queries).zip(Rx.Observable.from(responses))
		)
		.flatMap(([ query, response ]) => writeCache(query, response))
		.flatMap(() => Rx.Observable.empty());

/**
 * Listen for any action that should query the cache using a payload of queries
 *
 * Observables are heavily used in CACHE_REQUEST because each query results in
 * an async 'get' (Promise) from the Cache - all 'gets' happen in parallel and
 * the results are collated into a single response object containing the cache
 * hits.
 */
export const cacheQueryEpic = action$ =>
	action$.ofType('API_REQUEST')
		.flatMap(({ payload }) => Rx.Observable.from(payload))  // fan out
		.flatMap(readCache)                          // look for a cache hit
		.filter(([ query, response ]) => response)   // ignore misses
		.reduce((acc, [ query, response ]) => ({      // fan-in to create response
			queries: [ ...acc.queries, query ],
			responses: [ ...acc.responses, response ],
		}), { queries: [], responses: [] })           // empty response structure
		.map(cacheSuccess);

const cacheEpic = checkEnable() ? combineEpics(
	cacheClearEpic,
	cacheSetEpic,
	cacheQueryEpic
) : Rx.Observable.empty();

export default cacheEpic;

<commit_msg>remove old docs<commit_after>/**
 * Provides a cache outside of Redux state that can optimistically update state
 * before an asynchronous API call returns
 *
 * @module CacheMiddleware
 */
import Rx from 'rxjs';
import { combineEpics } from 'redux-observable';
import {
	cacheSuccess,
} from '../actions/cacheActionCreators';

import {
	makeCache,
	cacheReader,
	cacheWriter,
} from '../util/cacheUtils';

export function checkEnable() {
	if (typeof window !== 'undefined' && window.location) {
		const params = new URLSearchParams(window.location.search.slice(1));
		return !params.has('__nocache');
	}
	return true;
}

// get a cache, any cache (that conforms to the Promise-based API)
const cache = makeCache();

// get a function that can read from the cache for a specific query
const readCache = cacheReader(cache);
// get a function that can write to the cache for a specific query-response
const writeCache = cacheWriter(cache);

/**
 * Listen for any action that should clear cached state
 *
 * Note that this will clear the cache without emitting an action
 */
export const cacheClearEpic = action$ =>
	action$.ofType('LOGOUT_REQUEST', 'CACHE_CLEAR')
		.flatMap(() => cache.clear())  // wait for cache to clear before continuing
		.flatMap(() => Rx.Observable.empty());

/**
 * Listen for any action that should set cached state with a
 * `{ queries, responses }` payload
 *
 * API_SUCCESS means there is fresh data ready to be stored - extract the
 * queries and their responses, then dispatch `CACHE_SET` actions with each
 * pair
 *
 * Not that this will set the cache without emitting an action
 */
export const cacheSetEpic = action$ =>
	action$.ofType('API_SUCCESS', 'CACHE_SET')
		.flatMap(({ payload: { queries, responses } }) =>
			Rx.Observable.from(queries).zip(Rx.Observable.from(responses))
		)
		.flatMap(([ query, response ]) => writeCache(query, response))
		.flatMap(() => Rx.Observable.empty());

/**
 * Listen for any action that should query the cache using a payload of queries
 *
 * Observables are heavily used in CACHE_REQUEST because each query results in
 * an async 'get' (Promise) from the Cache - all 'gets' happen in parallel and
 * the results are collated into a single response object containing the cache
 * hits.
 */
export const cacheQueryEpic = action$ =>
	action$.ofType('API_REQUEST')
		.flatMap(({ payload }) => Rx.Observable.from(payload))  // fan out
		.flatMap(readCache)                          // look for a cache hit
		.filter(([ query, response ]) => response)   // ignore misses
		.reduce((acc, [ query, response ]) => ({      // fan-in to create response
			queries: [ ...acc.queries, query ],
			responses: [ ...acc.responses, response ],
		}), { queries: [], responses: [] })           // empty response structure
		.map(cacheSuccess);

const cacheEpic = checkEnable() ? combineEpics(
	cacheClearEpic,
	cacheSetEpic,
	cacheQueryEpic
) : Rx.Observable.empty();

export default cacheEpic;

<|endoftext|>


<commit_before>/**
 * @since 2016-09-02 13:30
 * @author vivaxy
 */

import React, { Component, PropTypes } from 'react';

export default class VerticalList extends Component {

    static propTypes = {
        children: PropTypes.arrayOf(PropTypes.shape({
            props: PropTypes.shape({
                flex: PropTypes.number,
                height: PropTypes.number,
            }).isRequired,
        }.isRequired).isRequired).isRequired,
    };

    render () {

        const boxStyle = {
            display: '-webkit-box',
            display: '-webkit-flex',
            display: 'flex',
            WebkitBoxOrient: 'vertical',
            WebkitBoxDirection: 'normal',
            WebkitFlexDirection: 'column',
            flexDirection: 'column',
        };

        const {
            children,
            style,
            ...otherProps
        } = this.props;

        return

            {children.map((child) => {

                const {
                    flex,
                    height,
                    style,
                    ...otherProps
                } = child.props;

                let childStyle = {};

                if (flex !== undefined) {
                    childStyle = {
                        WebkitBoxFlex: flex,
                        WebkitFlex: flex,
                        flex: flex,
                        ...style
                    };
                } else {
                    childStyle = {
                        height,
                        ...style
                    };
                }

                return
{child}

            })}

    }

}
<commit_msg>update VerticleList<commit_after>/**
 * @since 2016-09-02 13:30
 * @author vivaxy
 */

import React, { Component, cloneElement, PropTypes } from 'react';

export default class VerticalList extends Component {

    static propTypes = {
        children: PropTypes.arrayOf(PropTypes.shape({
            props: PropTypes.shape({
                flex: PropTypes.number,
                height: PropTypes.number,
            }).isRequired,
        }.isRequired).isRequired).isRequired,
    };

    render () {

        const boxStyle = {
            display: '-webkit-box',
            display: '-webkit-flex',
            display: 'flex',
            WebkitBoxOrient: 'vertical',
            WebkitBoxDirection: 'normal',
            WebkitFlexDirection: 'column',
            flexDirection: 'column',
        };

        const {
            children,
            style,
            ...otherProps
        } = this.props;

        return

            {children.map((child, index) => {

                const {
                    flex,
                    height,
                    style,
                    ...otherProps
                } = child.props;

                let childStyle = {};

                if (flex !== undefined) {
                    childStyle = {
                        WebkitBoxFlex: flex,
                        WebkitFlex: flex,
                        flex: flex,
                        ...style
                    };
                } else {
                    childStyle = {
                        height,
                        ...style
                    };
                }

                return cloneElement(child, {
                    style: childStyle,
                    key: `vertical-list-${index}`,
                    ...otherProps,
                });
            })}

    }
}
<|endoftext|>