<commit_before>const { resolve, dirname } = require('path');

const { isWindows, invalidWin32Path, o777 } = require('../util');
const { stat, mkdir } = require('../fs');

/**
 * @typedef {object} mkdirsOptions
 * @memberof nextra
 * @property {number} [mode = 0o777 & ~process.umask()] The chmod for the directories being made
 */

/**
 * Recursivly makes directories, until the directory passed exists.
 * @function ensureDir
 * @memberof nextra
 * @param {string} path The path you wish to make
 * @param {mkdirsOptions} [options] Options for making the directories
 * @param {string} [made = null] The path in progress, do not set.
 * @return {Promise<string>} The path made.
 */
/**
 * Recursivly makes directories, until the directory passed exists.
 * @function mkdirp
 * @memberof fsn/nextra
 * @param {string} path The path you wish to make
 * @param {mkdirsOptions} [options] Options for making the directories
 * @param {string} [made = null] The path in progress, do not set.
 * @return {Promise<string>} The path made.
 */
/**
 * Recursivly makes directories, until the directory passed exists.
 * @function mkdirs
 * @memberof fsn/nextra
 * @param {string} path The path you wish to make
 * @param {mkdirsOptions} [options] Options for making the directories
 * @param {string} [made = null] The path in progress, do not set.
 * @return {Promise<string>} The path made.
 */
module.exports = async function mkdirs(path, options, made = null) {
	if (!options || typeof options !== 'object') options = { mode: options };
	if (isWindows && invalidWin32Path(path)) {
		const errInval = new Error(`${path} contains invalid WIN32 path characters.`);
		errInval.code = 'EINVAL';
		throw errInval;
	}
	// eslint-disable-next-line no-bitwise
	const mode = options.mode || o777 & ~process.umask();
	path = resolve(path);
	return mkdir(path, mode)
		.then(() => made || path)
		.catch((err) => {
			if (err.code !== 'ENOENT') {
				return stat(path)
					.then(myStat => {
						if (myStat.isDirectory()) return made;
						throw err;
					});
			}
			if (dirname(path) === path) throw err;
			return mkdirs(dirname(path), options)
				.then(madeChain => mkdirs(path, options, madeChain));
		});
};
<commit_msg>fix some docs<commit_after>const { resolve, dirname } = require('path');

const { isWindows, invalidWin32Path, o777 } = require('../util');
const { stat, mkdir } = require('../fs');

/**
 * @typedef {object} mkdirsOptions
 * @memberof nextra
 * @property {number} [mode = 0o777 & ~process.umask()] The chmod for the directories being made
 */

/**
 * Recursively makes directories, until the directory passed exists.
 * @function ensureDir
 * @memberof fsn/nextra
 * @param {string} path The path you wish to make
 * @param {mkdirsOptions} [options] Options for making the directories
 * @param {string} [made = null] The path in progress, do not set.
 * @returns {Promise<string>} The path made.
 */
/**
 * Recursively makes directories, until the directory passed exists.
 * @function mkdirp
 * @memberof fsn/nextra
 * @param {string} path The path you wish to make
 * @param {mkdirsOptions} [options] Options for making the directories
 * @param {string} [made = null] The path in progress, do not set.
 * @returns {Promise<string>} The path made.
 */
/**
 * Recursively makes directories, until the directory passed exists.
 * @function mkdirs
 * @memberof fsn/nextra
 * @param {string} path The path you wish to make
 * @param {mkdirsOptions} [options] Options for making the directories
 * @param {string} [made = null] The path in progress, do not set.
 * @returns {Promise<string>} The path made.
 */
module.exports = async function mkdirs(path, options, made = null) {
	if (!options || typeof options !== 'object') options = { mode: options };
	if (isWindows && invalidWin32Path(path)) {
		const errInval = new Error(`${path} contains invalid WIN32 path characters.`);
		errInval.code = 'EINVAL';
		throw errInval;
	}
	// eslint-disable-next-line no-bitwise
	const mode = options.mode || o777 & ~process.umask();
	path = resolve(path);
	return mkdir(path, mode)
		.then(() => made || path)
		.catch((err) => {
			if (err.code !== 'ENOENT') {
				return stat(path)
					.then(myStat => {
						if (myStat.isDirectory()) return made;
						throw err;
					});
			}
			if (dirname(path) === path) throw err;
			return mkdirs(dirname(path), options)
				.then(madeChain => mkdirs(path, options, madeChain));
		});
};
<|endoftext|>


<commit_before>// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt

frappe.provide('erpnext');

// add toolbar icon
$(document).bind('toolbar_setup', function() {
	frappe.app.name = "ERPNext";

	frappe.help_feedback_link = '
Feedback
'

	$('.navbar-home').html('');

	$('[data-link="docs"]').attr("href", "https://frappe.github.io/erpnext/")
	$('[data-link="issues"]').attr("href", "https://github.com/frappe/erpnext/issues")

	// default documentation goes to erpnext
	// $('[data-link-type="documentation"]').attr('data-path', '/erpnext/manual/index');

	// additional help links for erpnext
	var $help_menu = $('.dropdown-help ul .documentation-links');

	$('
'+__('User Forum')+'
').insertBefore($help_menu);
	$('
'+__('Gitter Chat')+'
').insertBefore($help_menu);
	$('
'+__('Report an Issue')+'
').insertBefore($help_menu);

});

// doctypes created via tree
$.extend(frappe.create_routes, {
	"Customer Group": "Tree/Customer Group",
	"Territory": "Tree/Territory",
	"Item Group": "Tree/Item Group",
	"Sales Person": "Tree/Sales Person",
	"Account": "Tree/Account",
	"Cost Center": "Tree/Cost Center"
});

// preferred modules for breadcrumbs
$.extend(frappe.breadcrumbs.preferred, {
	"Item Group": "Stock",
	"Customer Group": "Selling",
	"Supplier Group": "Buying",
	"Territory": "Selling",
	"Sales Person": "Selling",
	"Sales Partner": "Selling",
	"Brand": "Selling"
});
<commit_msg>[feat] Modified help config<commit_after>// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt

frappe.provide('erpnext');

// add toolbar icon
$(document).bind('toolbar_setup', function() {
	frappe.app.name = "ERPNext";

	frappe.help_feedback_link = '
Feedback
'

	$('.navbar-home').html('');

	$('[data-link="docs"]').attr("href", "https://erpnext.com/docs")
	$('[data-link="issues"]').attr("href", "https://github.com/frappe/erpnext/issues")

	// default documentation goes to erpnext
	// $('[data-link-type="documentation"]').attr('data-path', '/erpnext/manual/index');

	// additional help links for erpnext
	var $help_menu = $('.dropdown-help ul .documentation-links');
	$('
'+__('Documentation')+'
').insertBefore($help_menu);
	$('
'+__('User Forum')+'
').insertBefore($help_menu);
	$('
'+__('Gitter Chat')+'
').insertBefore($help_menu);
	$('
'+__('Report an Issue')+'
').insertBefore($help_menu);

});

// doctypes created via tree
$.extend(frappe.create_routes, {
	"Customer Group": "Tree/Customer Group",
	"Territory": "Tree/Territory",
	"Item Group": "Tree/Item Group",
	"Sales Person": "Tree/Sales Person",
	"Account": "Tree/Account",
	"Cost Center": "Tree/Cost Center"
});

// preferred modules for breadcrumbs
$.extend(frappe.breadcrumbs.preferred, {
	"Item Group": "Stock",
	"Customer Group": "Selling",
	"Supplier Group": "Buying",
	"Territory": "Selling",
	"Sales Person": "Selling",
	"Sales Partner": "Selling",
	"Brand": "Selling"
});
<|endoftext|>


<commit_before>let OptionsPanel = (function() {
  let instance = null;

  class OptionsPanel {
    constructor(nodes) {
      if (instance) return instance;
      instance = this;

      this.config = new Config();
      this.namesPanel = new NamesPanel();

      nodes.optionsBtn.addEventListener('click', e => {
        this.open();
      });

      nodes.optionsForm.addEventListener('submit', e => {
        this.save();
        this.namesPanel.init(true);
        this.close();
      });

      nodes.options_optionCancel.addEventListener('click', e => {
        this.init();
        this.close();
      });

      [].forEach.call(nodes.options_checkboxes, aChkbox => {
        aChkbox.addEventListener('click', function(e) {
          let thisChkbox = this.querySelector('input[type=checkbox]');
          thisChkbox.checked = !thisChkbox.checked;
          if (thisChkbox.checked) this.classList.add('on');
          else this.classList.remove('on');
        }, false);
      });
    }

    init() {
      nodes.options_names.value = this.config.names;
      nodes.options_removeAfterHit.checked = this.config.removeAfterHit;
      nodes.options_stopOnDemand.checked = this.config.stopOnDemand;
      nodes.options_lightTheme.checked = this.config.lightTheme;
      nodes.options_readOutNames.checked = this.config.readOutNames;
      [].forEach.call(nodes.options_checkboxes, aChkbox => {
        let thisChkbox = aChkbox.querySelector('input[type=checkbox]');
        if (thisChkbox.checked) aChkbox.classList.add('on');
        else aChkbox.classList.remove('on');
      });
    }

    save() {
       this.config.names = nodes.options_names.value;
       this.config.removeAfterHit = nodes.options_removeAfterHit.checked;
       this.config.stopOnDemand = nodes.options_stopOnDemand.checked;
       this.config.lightTheme = nodes.options_lightTheme.checked;
       this.config.readOutNames = nodes.options_readOutNames.checked;
       this.config.save();
    }

    open() {
      nodes.wrapper.classList.add('flip');
    }

    close() {
      nodes.wrapper.classList.remove('flip');
    }
  }

  return OptionsPanel;
})();<commit_msg>Use array iterator<commit_after>let OptionsPanel = (function() {
  let instance = null;

  class OptionsPanel {
    constructor(nodes) {
      if (instance) return instance;
      instance = this;

      this.config = new Config();
      this.namesPanel = new NamesPanel();

      nodes.optionsBtn.addEventListener('click', e => {
        this.open();
      });

      nodes.optionsForm.addEventListener('submit', e => {
        this.save();
        this.namesPanel.init(true);
        this.close();
      });

      nodes.options_optionCancel.addEventListener('click', e => {
        this.init();
        this.close();
      });

      nodes.options_checkboxes.forEach(aChkbox => {
        aChkbox.addEventListener('click', function(e) {
          let realChkbox = this.querySelector('input[type=checkbox]');
          realChkbox.checked = !realChkbox.checked;
          realChkbox.checked ? this.classList.add('on') : this.classList.remove('on');
        }, false);
      });
    }

    init() {
      nodes.options_names.value = this.config.names;
      nodes.options_removeAfterHit.checked = this.config.removeAfterHit;
      nodes.options_stopOnDemand.checked = this.config.stopOnDemand;
      nodes.options_lightTheme.checked = this.config.lightTheme;
      nodes.options_readOutNames.checked = this.config.readOutNames;
      nodes.options_checkboxes.forEach(aChkbox => {
        let realChkbox = aChkbox.querySelector('input[type=checkbox]');
        realChkbox.checked ? aChkbox.classList.add('on') : aChkbox.classList.remove('on');
      });
    }

    save() {
       this.config.names = nodes.options_names.value;
       this.config.removeAfterHit = nodes.options_removeAfterHit.checked;
       this.config.stopOnDemand = nodes.options_stopOnDemand.checked;
       this.config.lightTheme = nodes.options_lightTheme.checked;
       this.config.readOutNames = nodes.options_readOutNames.checked;
       this.config.save();
    }

    open() {
      nodes.wrapper.classList.add('flip');
    }

    close() {
      nodes.wrapper.classList.remove('flip');
    }
  }

  return OptionsPanel;
})();
<|endoftext|>


<commit_before>import React from 'react';
import Interest from '../components/InterestSignupForm';
import B from '../assets/images/b.png';
import MLHBadge from '../components/MLHBadge';
import FAQ from './Info/FAQ';
import Animation from './Animation';

const Landing = () => (

      <MLHBadge className="mlh" />
      <Animation />

    <FAQ />

);

export default Landing;
<commit_msg>fixed travis issues<commit_after>import React from 'react';
import MLHBadge from '../components/MLHBadge';
import FAQ from './Info/FAQ';
import Animation from './Animation';

const Landing = () => (

      <MLHBadge className="mlh" />
      <Animation />

    <FAQ />

);

export default Landing;
<|endoftext|>


<commit_before>/*! Palindrom
 * https://github.com/Palindrom/Palindrom
 * (c) 2017 Joachim Wester
 * MIT license
 */
const Palindrom = require('./palindrom');

const PalindromDOM = (() => {
    /** scroll to coordiates and return if the scroll was successful */
    function attemptScroll(x, y) {
        scrollTo(x, y);
        return window.scrollX === x && window.scrollY === y;
    }

    async function sleep(duration) {
        return new Promise(resolve => {
            setTimeout(resolve, duration);
        });
    }

    /**
     * PalindromDOM
     * @extends {Palindrom}
     * @param {Object} [options] map of arguments. See README.md for description
     */
    class PalindromDOM extends Palindrom {
        constructor(options) {
            if (typeof options !== 'object') {
                throw new Error(
                    'PalindromDOM constructor requires an object argument.'
                );
            }
            if (!options.remoteUrl) {
                throw new Error('remoteUrl is required');
            }
            const onStateReset = options.onStateReset || options.callback;
            if (options.callback) {
                console.warn(
                    'Palindrom: options.callback is deprecated. Please use `onStateReset` instead'
                );
            }

            options.onStateReset = function addDOMListeners(obj) {
                this.listen();
                onStateReset && onStateReset.call(this, obj);
            };

            // construct Palindrom
            super(options);

            this.element = options.listenTo || document;
            this.clickHandler = this.clickHandler.bind(this);
            this.historyHandler = this.historyHandler.bind(this);
            this.morphUrlEventHandler = this.morphUrlEventHandler.bind(this);

            /* in some cases, people emit redirect requests before `listen` is called */
            this.element.addEventListener(
                'palindrom-redirect-pushstate',
                this.historyHandler
            );

            if ('scrollRestoration' in history) {
                history.scrollRestoration = 'manual';
            }
        }

        listen() {
            this.listening = true;
            this.element.addEventListener('click', this.clickHandler);
            window.addEventListener('popstate', this.historyHandler); //better here than in constructor, because Chrome triggers popstate on page load

            this.element.addEventListener(
                'palindrom-morph-url',
                this.morphUrlEventHandler
            );

            this.element.addEventListener(
                'palindrom-redirect-pushstate',
                this.historyHandler
            );
        }
        unlisten() {
            this.listening = false;

            this.element.removeEventListener('click', this.clickHandler);
            window.removeEventListener('popstate', this.historyHandler); //better here than in constructor, because Chrome triggers popstate on page load
            this.element.removeEventListener(
                'palindrom-redirect-pushstate',
                this.historyHandler
            );

            this.element.removeEventListener(
                'palindrom-morph-url',
                this.morphUrlEventHandler
            );
        }

        /**
         * @param {String} href
         * @param {boolean} [throwOnCancellation=true] whether to throw an error when `palindrom-before-redirect`'s default behaviour is prevented using event.preventDefault(), defaults to `true`
         * @throws {Error} network error if occured or the `palindrom-before-redirect` was cancelled by calling event.preventDefault()
         * @fires Palindrom#palindrom-before-redirect
         * @fires Palindrom#palindrom-after-redirect
         * @returns {Response} response (https://github.com/axios/axios#response-schema)
         */
        async getPatchUsingHTTP(href, throwOnCancellation = true) {
            /**
             * palindrom-before-redirect event.
             *
             * @event Palindrom#palindrom-before-redirect
             * @type {CustomEvent}
             * @property {Object} detail containing `href` property that contains the URL
             */
            const beforeEvent = new CustomEvent('palindrom-before-redirect', {
                detail: {
                    href
                },
                cancelable: true,
                bubbles: true
            });

            this.element.dispatchEvent(beforeEvent);

            if (beforeEvent.defaultPrevented) {
                // We only throw errors on people who take matters into their own hands and use getPatchUsingHTTP (not morphUrl)
                if (throwOnCancellation) {
                    throw new Error(
                        '`getPatchUsingHTTP` was aborted by cancelling `palindrom-before-redirect` event.'
                    );
                } else {
                    return false;
                }
            }

            const response = await this.network.getPatchUsingHTTP(href);
            let detail = { href, response };

            /**
             * palindrom-after-redirect event
             *
             * @event Palindrom#palindrom-after-redirect
             * @type {CustomEvent}
             * @property {Object} detail containing `href: String` and `response: Response (https://developer.mozilla.org/en-US/docs/Web/API/Response)`
             */
            const afterEvent = new CustomEvent('palindrom-after-redirect', {
                detail,
                bubbles: true
            });
            this.element.dispatchEvent(afterEvent);
            return response;
        }

        //TODO move fallback to window.location.href from PalindromNetworkChannel to here (PalindromDOM)

        /**
     * DISABLED FOR NOW: we don't know when rendering actually finishes.
     * It's left here for the hope of having synchronous rendering at some point in the future.
     * ====
     * we need to scroll asynchronously, because we need the document rendered to search for the anchored element
     * and even though onReceive + applyPatch are sync, Polymer is not, it renders async-ly
    PalindromDOM.prototype.scrollToAnchorOrTopAsync = function(link) {
      this.scrollAsyncTimeout && clearTimeout(this.scrollAsyncTimeout);
      if (window && window.document) {
        var anchorIndex;
        var anchor;
        // does the URL have an anchor
        if (link && (anchorIndex = link.indexOf('#')) > -1) {
          anchor = link.substr(anchorIndex);
        }
        if (!anchor) {
          window.scrollTo(0, 0);
        } else {
          // if somehow someone manages to navigate twice in a 100ms,
          // we don't scroll for their first navigation, i.e de-bouncing

          this.scrollAsyncTimeout = setTimeout(() => {
            // does that anchor exist in the page?
            const anchorTarget = document.querySelector(anchor); // look for #element-id
            if (anchorTarget) {
              anchorTarget.scrollIntoView();
            } else {
              window.scrollTo(0, 0);
            }
          }, 100);
        }
      }
    };
    */
        /**
         * Push a new URL to the browser address bar and send a patch request (empty or including queued local patches)
         * so that the URL handlers can be executed on the remote
         * @param url
         * @returns {boolean} true if morphing was successful
         */
        async morphUrl(url) {
            const scrollX = window.scrollX;
            const scrollY = window.scrollY;
            const res = await this.getPatchUsingHTTP(url, false);
            if (res && res.status < 500) {
                // mark current state's scroll position
                history.replaceState(
                    [scrollX, scrollY],
                    null,
                    window.location.href
                );

                // push a new state with the new position
                history.pushState([0, 0], null, url);

                // scroll it!
                scrollTo(0, 0);
                return true;
            } else {
                return false;
            }
        }

        /**
         * Handles `palindrom-morph-url` event and channels its `detail.url` to `morphUrl`
         * @param {palindrom-morph-url Event} event
         */
        morphUrlEventHandler(event) {
            this.morphUrl(event.detail.url);
        }

        clickHandler(event) {
            //Don't morph ctrl/cmd + click & middle mouse button
            if (event.ctrlKey || event.metaKey || event.which == 2) {
                return;
            }

            if (event.detail && event.detail.target) {
                //detail is Polymer
                event = event.detail;
            }

            let target = event.target;

            if (target.nodeName !== 'A') {
                let eventPath = event.composedPath && event.composedPath();
                if (!eventPath) {
                    // for backwards compatibility with SDv0
                    eventPath = event.path;
                }
                for (let i = 0; i < eventPath.length; i++) {
                    if (eventPath[i].nodeName == 'A') {
                        target = eventPath[i];
                        break;
                    }
                }
            }
            const anchorTarget = target.target || target.getAttribute('target');
            const hasDownloadAttribute = target.hasAttribute('download');

            if (
                !hasDownloadAttribute &&
                (!anchorTarget || anchorTarget === '_self')
            ) {
                //needed since Polymer 0.2.0 in Chrome stable / Web Plaftorm features disabled
                //because target.href returns undefined for <polymer-ui-menu-item href="..."> (which is an error)
                //while target.getAttribute("href") returns desired href (as string)
                const href = target.href || target.getAttribute('href');
                if (href && PalindromDOM.isApplicationLink(href)) {
                    event.preventDefault();
                    event.stopPropagation();
                    this.morphUrl(href);
                } else if (target.type === 'submit') {
                    event.preventDefault();
                }
            }
        }

        async historyHandler(event) {
            await this.getPatchUsingHTTP(location.href);
            const [scrollX, scrollY] = event.state || [0, 0];
            let hadScrolled = false;
            const scrollHandler = () => (hadScrolled = true);
            window.addEventListener('scroll', scrollHandler);
            for (let i = 0; i < 30 && !hadScrolled; i++) {
                if (attemptScroll(scrollX, scrollY)) {
                    break;
                } else {
                    await sleep(30);
                }
            }
            window.removeEventListener('scroll', scrollHandler);
        }

        /**
         * Returns information if a given element is an internal application link that Palindrom should intercept into a history push
         * @param elem HTMLElement or String
         * @returns {boolean}
         */
        static isApplicationLink(elem) {
            if (typeof elem === 'string') {
                //type string is reported in Polymer / Canary (Web Platform features disabled)
                const parser = document.createElement('A');
                parser.href = elem;

                // @see http://stackoverflow.com/questions/736513/how-do-i-parse-a-url-into-hostname-and-path-in-javascript
                // IE doesn't populate all link properties when setting .href with a relative URL,
                // however .href will return an absolute URL which then can be used on itself
                // to populate these additional fields.
                if (parser.host == '') {
                    parser.href = parser.href;
                }
                elem = parser;
            }
            return (
                elem.protocol == window.location.protocol &&
                elem.host == window.location.host
            );
        }
    }

    return PalindromDOM;
})();

module.exports = PalindromDOM;
module.exports.default = PalindromDOM;
module.exports.__esModule = true;
<commit_msg>Do not mistake programatic scroll for organic scroll<commit_after>/*! Palindrom
 * https://github.com/Palindrom/Palindrom
 * (c) 2017 Joachim Wester
 * MIT license
 */
const Palindrom = require('./palindrom');

const PalindromDOM = (() => {
    /** scroll to coordiates and return if the scroll was successful */
    function attemptScroll(x, y) {
        scrollTo(x, y);
        return window.scrollX === x && window.scrollY === y;
    }

    async function sleep(duration) {
        return new Promise(resolve => {
            setTimeout(resolve, duration);
        });
    }

    /**
     * PalindromDOM
     * @extends {Palindrom}
     * @param {Object} [options] map of arguments. See README.md for description
     */
    class PalindromDOM extends Palindrom {
        constructor(options) {
            if (typeof options !== 'object') {
                throw new Error(
                    'PalindromDOM constructor requires an object argument.'
                );
            }
            if (!options.remoteUrl) {
                throw new Error('remoteUrl is required');
            }
            const onStateReset = options.onStateReset || options.callback;
            if (options.callback) {
                console.warn(
                    'Palindrom: options.callback is deprecated. Please use `onStateReset` instead'
                );
            }

            options.onStateReset = function addDOMListeners(obj) {
                this.listen();
                onStateReset && onStateReset.call(this, obj);
            };

            // construct Palindrom
            super(options);

            this.element = options.listenTo || document;
            this.clickHandler = this.clickHandler.bind(this);
            this.historyHandler = this.historyHandler.bind(this);
            this.morphUrlEventHandler = this.morphUrlEventHandler.bind(this);

            /* in some cases, people emit redirect requests before `listen` is called */
            this.element.addEventListener(
                'palindrom-redirect-pushstate',
                this.morphUrlEventHandler
            );

            if ('scrollRestoration' in history) {
                history.scrollRestoration = 'manual';
            }
        }

        listen() {
            this.listening = true;
            this.element.addEventListener('click', this.clickHandler);
            window.addEventListener('popstate', this.historyHandler); //better here than in constructor, because Chrome triggers popstate on page load

            this.element.addEventListener(
                'palindrom-morph-url',
                this.morphUrlEventHandler
            );

            this.element.addEventListener(
                'palindrom-redirect-pushstate',
                this.historyHandler
            );
        }
        unlisten() {
            this.listening = false;

            this.element.removeEventListener('click', this.clickHandler);
            window.removeEventListener('popstate', this.historyHandler); //better here than in constructor, because Chrome triggers popstate on page load
            this.element.removeEventListener(
                'palindrom-redirect-pushstate',
                this.historyHandler
            );

            this.element.removeEventListener(
                'palindrom-morph-url',
                this.morphUrlEventHandler
            );
        }

        /**
         * @param {String} href
         * @param {boolean} [throwOnCancellation=true] whether to throw an error when `palindrom-before-redirect`'s default behaviour is prevented using event.preventDefault(), defaults to `true`
         * @throws {Error} network error if occured or the `palindrom-before-redirect` was cancelled by calling event.preventDefault()
         * @fires Palindrom#palindrom-before-redirect
         * @fires Palindrom#palindrom-after-redirect
         * @returns {Response} response (https://github.com/axios/axios#response-schema)
         */
        async getPatchUsingHTTP(href, throwOnCancellation = true) {
            /**
             * palindrom-before-redirect event.
             *
             * @event Palindrom#palindrom-before-redirect
             * @type {CustomEvent}
             * @property {Object} detail containing `href` property that contains the URL
             */
            const beforeEvent = new CustomEvent('palindrom-before-redirect', {
                detail: {
                    href
                },
                cancelable: true,
                bubbles: true
            });

            this.element.dispatchEvent(beforeEvent);

            if (beforeEvent.defaultPrevented) {
                // We only throw errors on people who take matters into their own hands and use getPatchUsingHTTP (not morphUrl)
                if (throwOnCancellation) {
                    throw new Error(
                        '`getPatchUsingHTTP` was aborted by cancelling `palindrom-before-redirect` event.'
                    );
                } else {
                    return false;
                }
            }

            const response = await this.network.getPatchUsingHTTP(href);
            let detail = { href, response };

            /**
             * palindrom-after-redirect event
             *
             * @event Palindrom#palindrom-after-redirect
             * @type {CustomEvent}
             * @property {Object} detail containing `href: String` and `response: Response (https://developer.mozilla.org/en-US/docs/Web/API/Response)`
             */
            const afterEvent = new CustomEvent('palindrom-after-redirect', {
                detail,
                bubbles: true
            });
            this.element.dispatchEvent(afterEvent);
            return response;
        }

        //TODO move fallback to window.location.href from PalindromNetworkChannel to here (PalindromDOM)

        /**
     * DISABLED FOR NOW: we don't know when rendering actually finishes.
     * It's left here for the hope of having synchronous rendering at some point in the future.
     * ====
     * we need to scroll asynchronously, because we need the document rendered to search for the anchored element
     * and even though onReceive + applyPatch are sync, Polymer is not, it renders async-ly
    PalindromDOM.prototype.scrollToAnchorOrTopAsync = function(link) {
      this.scrollAsyncTimeout && clearTimeout(this.scrollAsyncTimeout);
      if (window && window.document) {
        var anchorIndex;
        var anchor;
        // does the URL have an anchor
        if (link && (anchorIndex = link.indexOf('#')) > -1) {
          anchor = link.substr(anchorIndex);
        }
        if (!anchor) {
          window.scrollTo(0, 0);
        } else {
          // if somehow someone manages to navigate twice in a 100ms,
          // we don't scroll for their first navigation, i.e de-bouncing

          this.scrollAsyncTimeout = setTimeout(() => {
            // does that anchor exist in the page?
            const anchorTarget = document.querySelector(anchor); // look for #element-id
            if (anchorTarget) {
              anchorTarget.scrollIntoView();
            } else {
              window.scrollTo(0, 0);
            }
          }, 100);
        }
      }
    };
    */
        /**
         * Push a new URL to the browser address bar and send a patch request (empty or including queued local patches)
         * so that the URL handlers can be executed on the remote
         * @param url
         * @returns {boolean} true if morphing was successful
         */
        async morphUrl(url) {
            const scrollX = window.scrollX;
            const scrollY = window.scrollY;
            const res = await this.getPatchUsingHTTP(url, false);
            if (res && res.status < 500) {
                // mark current state's scroll position
                history.replaceState(
                    [scrollX, scrollY],
                    null,
                    window.location.href
                );

                // push a new state with the new position
                history.pushState([0, 0], null, url);

                // scroll it!
                scrollTo(0, 0);
                return true;
            } else {
                return false;
            }
        }

        /**
         * Handles `palindrom-morph-url` event and channels its `detail.url` to `morphUrl`
         * @param {palindrom-morph-url Event} event
         */
        morphUrlEventHandler(event) {
            this.morphUrl(event.detail.url);
        }

        clickHandler(event) {
            //Don't morph ctrl/cmd + click & middle mouse button
            if (event.ctrlKey || event.metaKey || event.which == 2) {
                return;
            }

            if (event.detail && event.detail.target) {
                //detail is Polymer
                event = event.detail;
            }

            let target = event.target;

            if (target.nodeName !== 'A') {
                let eventPath = event.composedPath && event.composedPath();
                if (!eventPath) {
                    // for backwards compatibility with SDv0
                    eventPath = event.path;
                }
                for (let i = 0; i < eventPath.length; i++) {
                    if (eventPath[i].nodeName == 'A') {
                        target = eventPath[i];
                        break;
                    }
                }
            }
            const anchorTarget = target.target || target.getAttribute('target');
            const hasDownloadAttribute = target.hasAttribute('download');

            if (
                !hasDownloadAttribute &&
                (!anchorTarget || anchorTarget === '_self')
            ) {
                //needed since Polymer 0.2.0 in Chrome stable / Web Plaftorm features disabled
                //because target.href returns undefined for <polymer-ui-menu-item href="..."> (which is an error)
                //while target.getAttribute("href") returns desired href (as string)
                const href = target.href || target.getAttribute('href');
                if (href && PalindromDOM.isApplicationLink(href)) {
                    event.preventDefault();
                    event.stopPropagation();
                    this.morphUrl(href);
                } else if (target.type === 'submit') {
                    event.preventDefault();
                }
            }
        }

        async historyHandler(event) {
            await this.getPatchUsingHTTP(location.href);
            const [scrollX, scrollY] = event.state || [0, 0];
            // flag if the user has scrolled, not our own code
            let userHadScrolled = false;

            // flag if this code it scrolling, not the user
            let attemptingScroll = false;

            // if this handler is called && we're not attemptingScroll, then the user has scrolled!
            const scrollHandler = () => (userHadScrolled = !attemptingScroll);
            window.addEventListener('scroll', scrollHandler);

            for (let i = 0; i < 30 && !userHadScrolled; i++) {
                // prevent our scroll attempt from setting `hadScrolled`
                attemptingScroll = true;
                const scrollSucceeded = attemptScroll(scrollX, scrollY);
                attemptingScroll = false;
                if (scrollSucceeded) {
                    break;
                } else {
                    await sleep(30);
                }
            }
            window.removeEventListener('scroll', scrollHandler);
        }

        /**
         * Returns information if a given element is an internal application link that Palindrom should intercept into a history push
         * @param elem HTMLElement or String
         * @returns {boolean}
         */
        static isApplicationLink(elem) {
            if (typeof elem === 'string') {
                //type string is reported in Polymer / Canary (Web Platform features disabled)
                const parser = document.createElement('A');
                parser.href = elem;

                // @see http://stackoverflow.com/questions/736513/how-do-i-parse-a-url-into-hostname-and-path-in-javascript
                // IE doesn't populate all link properties when setting .href with a relative URL,
                // however .href will return an absolute URL which then can be used on itself
                // to populate these additional fields.
                if (parser.host == '') {
                    parser.href = parser.href;
                }
                elem = parser;
            }
            return (
                elem.protocol == window.location.protocol &&
                elem.host == window.location.host
            );
        }
    }

    return PalindromDOM;
})();

module.exports = PalindromDOM;
module.exports.default = PalindromDOM;
module.exports.__esModule = true;<|endoftext|>


<commit_before>/*
 * Paper.js
 *
 * This file is part of Paper.js, a JavaScript Vector Graphics Library,
 * based on Scriptographer.org and designed to be largely API compatible.
 * http://paperjs.org/
 * http://scriptographer.org/
 *
 * Copyright (c) 2011, Juerg Lehni & Jonathan Puckey
 * http://lehni.org/ & http://jonathanpuckey.com/
 *
 * Distributed under the MIT license. See LICENSE file for details.
 *
 * All rights reserved.
 */

/**
 * @name PathItem
 *
 * @class The PathItem class is the base for any items that describe paths
 * and offer standardised methods for drawing and path manipulation, such as
 * {@link Path} and {@link CompoundPath}.
 *
 * @extends Item
 */
var PathItem = this.PathItem = Item.extend(/** @lends PathItem# */{
	// Note: The PathItem class is currently empty but holds the documentation
	// for all the methods that exist both on Path and CompoundPath.
	/**
	 * Smooth bezier curves without changing the amount of segments or their
	 * points, by only smoothing and adjusting their handle points, for both
	 * open ended and closed paths.
	 *
	 * @name PathItem#smooth
	 * @function
	 *
	 * @example {@paperscript}
	 * // Smoothing a closed shape:
	 *
	 * // Create a rectangular path with its top-left point at
	 * // {x: 30, y: 25} and a size of {width: 50, height: 50}:
	 * var path = new Path.Rectangle(new Point(30, 25), new Size(50, 50));
	 * path.strokeColor = 'black';
	 *
	 * // Select the path, so we can see its handles:
	 * path.selected = true;
	 *
	 * // Create a copy of the path and move it 100pt to the right:
	 * var copy = path.clone();
	 * copy.position.x += 100;
	 *
	 * // Smooth the segments of the copy:
	 * copy.smooth();
	 *
	 * @example {@paperscript height=220}
	 * var path = new Path();
	 * path.strokeColor = 'black';
	 *
	 * path.add(new Point(30, 50));
	 *
	 * var y = 5;
	 * var x = 3;
	 *
	 * for (var i = 0; i < 28; i++) {
	 *     y *= -1.1;
	 *     x *= 1.1;
	 *     path.lineBy(x, y);
	 * }
	 *
	 * // Create a copy of the path and move it 100pt down:
	 * var copy = path.clone();
	 * copy.position.y += 120;
	 *
	 * // Set its stroke color to red:
	 * copy.strokeColor = 'red';
	 *
	 * // Smooth the segments of the copy:
	 * copy.smooth();
	 */

	/**
	 * {@grouptitle Postscript Style Drawing Commands}
	 *
	 * If called on a {@link CompoundPath}, a new {@link Path} is created as a
	 * child and the point is added as its first segment. On a normal empty
	 * {@link Path}, the point is simply added as its first segment.
	 *
	 * @name PathItem#moveTo
	 * @function
	 * @param {Point} point
	 */

	// DOCS: Document lineTo
	/**
	 * @name PathItem#lineTo
	 * @function
	 * @param {Point} point
	 */

	/**
	 * Adds a cubic bezier curve to the path, defined by two handles and a to
	 * point.
	 *
	 * @name PathItem#cubicCurveTo
	 * @function
	 * @param {Point} handle1
	 * @param {Point} handle2
	 * @param {Point} to
	 */

	/**
	 * Adds a quadratic bezier curve to the path, defined by a handle and a to
	 * point.
	 *
	 * @name PathItem#quadraticCurveTo
	 * @function
	 * @param {Point} handle
	 * @param {Point} to
	 */

	// DOCS: Document PathItem#curveTo 'paramater' param.
	/**
	 * Draws a curve from the position of the last segment point in the path
	 * that goes through the specified {@code through} point, to the specified
	 * {@code to} point by adding one segment to the path.
	 *
	 * @name PathItem#curveTo
	 * @function
	 * @param {Point} through the point through which the curve should go
	 * @param {Point} to the point where the curve should end
	 * @param {Number} [parameter=0.5]
	 *
	 * @example {@paperscript height=300}
	 * // Interactive example. Click and drag in the view below:
	 *
	 * var myPath;
	 * function onMouseDrag(event) {
	 * 	// If we created a path before, remove it:
	 * 	if (myPath) {
	 * 	    myPath.remove();
	 * 	}
	 *
	 * 	// Create a new path and add a segment point to it
	 * 	// at {x: 150, y: 150):
	 * 	myPath = new Path();
	 * 	myPath.add(150, 150);
	 *
	 * 	// Draw a curve through the position of the mouse to 'toPoint'
	 * 	var toPoint = new Point(350, 150);
	 * 	myPath.curveTo(event.point, toPoint);
	 *
	 * 	// Select the path, so we can see its segments:
	 * 	myPath.selected = true;
	 * }
     *
	 * // When the mouse is released, deselect the path
	 * // and set its stroke-color to black:
	 * function onMouseUp(event) {
	 * 	myPath.selected = false;
	 * 	myPath.strokeColor = 'black';
	 * }
	 */

	/**
	 * Draws an arc from the position of the last segment point in the path that
	 * goes through the specified {@code through} point, to the specified
	 * {@code to} point by adding one or more segments to the path.
	 *
	 * @name PathItem#arcTo
	 * @function
	 * @param {Point} through the point where the arc should pass through
	 * @param {Point} to the point where the arc should end
	 *
	 * @example {@paperscript}
	 * var path = new Path();
	 * path.strokeColor = 'black';
	 *
	 * var firstPoint = new Point(30, 75);
	 * path.add(firstPoint);
	 *
	 * // The point through which we will create the arc:
	 * var throughPoint = new Point(40, 40);
	 *
	 * // The point at which the arc will end:
	 * var toPoint = new Point(130, 75);
	 *
	 * // Draw an arc through 'throughPoint' to 'toPoint'
	 * path.arcTo(throughPoint, toPoint);
	 *
	 * // Add a red circle shaped path at the position of 'throughPoint':
	 * var circle = new Path.Circle(throughPoint, 3);
	 * circle.fillColor = 'red';
	 *
	 * @example {@paperscript height=300}
	 * // Interactive example. Click and drag in the view below:
	 *
	 * var myPath;
	 * function onMouseDrag(event) {
	 * 	// If we created a path before, remove it:
	 * 	if (myPath) {
	 * 	    myPath.remove();
	 * 	}
	 *
	 * 	// Create a new path and add a segment point to it
	 * 	// at {x: 150, y: 150):
	 * 	myPath = new Path();
	 * 	myPath.add(150, 150);
	 *
	 * 	// Draw an arc through the position of the mouse to 'toPoint'
	 * 	var toPoint = new Point(350, 150);
	 * 	myPath.arcTo(event.point, toPoint);
	 *
	 * 	// Select the path, so we can see its segments:
	 * 	myPath.selected = true;
	 * }
	 *
	 * // When the mouse is released, deselect the path
	 * // and fill it with black.
	 * function onMouseUp(event) {
	 * 	myPath.selected = false;
	 * 	myPath.fillColor = 'black';
	 * }
	 */
	/**
	 * Draws an arc from the position of the last segment point in the path to
	 * the specified point by adding one or more segments to the path.
	 *
	 * @name PathItem#arcTo
	 * @function
	 * @param {Point} to the point where the arc should end
	 * @param {Boolean} [clockwise=true] specifies whether the arc should be
	 *        drawn in clockwise direction.
	 *
	 * @example {@paperscript}
	 * var path = new Path();
	 * path.strokeColor = 'black';
	 *
	 * path.add(new Point(30, 75));
	 * path.arcTo(new Point(130, 75));
	 *
	 * var path2 = new Path();
	 * path2.strokeColor = 'red';
	 * path2.add(new Point(180, 25));
	 *
	 * // To draw an arc in anticlockwise direction,
	 * // we pass 'false' as the second argument to arcTo:
	 * path2.arcTo(new Point(280, 25), false);
	 *
	 * @example {@paperscript height=300}
	 * // Interactive example. Click and drag in the view below:
	 * var myPath;
	 *
	 * // The mouse has to move at least 20 points before
	 * // the next mouse drag event is fired:
	 * tool.minDistance = 20;
	 *
	 * // When the user clicks, create a new path and add
	 * // the current mouse position to it as its first segment:
	 * function onMouseDown(event) {
	 * 	myPath = new Path();
	 * 	myPath.strokeColor = 'black';
	 * 	myPath.add(event.point);
	 * }
	 *
	 * // On each mouse drag event, draw an arc to the current
	 * // position of the mouse:
	 * function onMouseDrag(event) {
	 * 	myPath.arcTo(event.point);
	 * }
	 */

	/**
	 * Closes the path. When closed, Paper.js connects the first and last
	 * segments.
	 *
	 * @name PathItem#closePath
	 * @function
	 * @see Path#closed
	 */

	/**
	 * {@grouptitle Relative Drawing Commands}
	 *
	 * If called on a {@link CompoundPath}, a new {@link Path} is created as a
	 * child and the point is added as its first segment relative to the
	 * position of the last segment of the current path.
	 *
	 * @name PathItem#moveBy
	 * @function
	 * @param {Point} point
	 */

	/**
	 * Adds a segment relative to the last segment point of the path.
	 *
	 * @name PathItem#lineBy
	 * @function
	 * @param {Point} vector The vector which is added to the position of the
	 *        last segment of the path, to become the new segment.
	 *
	 * @example {@paperscript}
	 * var path = new Path();
	 * path.strokeColor = 'black';
	 *
	 * // Add a segment at {x: 50, y: 50}
	 * path.add(25, 25);
	 *
	 * // Add a segment relative to the last segment of the path.
	 * // 50 in x direction and 0 in y direction, becomes {x: 75, y: 25}
	 * path.lineBy(50, 0);
	 *
	 * // 0 in x direction and 50 in y direction, becomes {x: 75, y: 75}
	 * path.lineBy(0, 50);
	 *
	 * @example {@paperscript height=300}
	 * // Drawing a spiral using lineBy:
	 * var path = new Path();
	 * path.strokeColor = 'black';
	 *
	 * // Add the first segment at {x: 50, y: 50}
	 * path.add(view.center);
	 *
	 * // Loop 500 times:
	 * for (var i = 0; i < 500; i++) {
	 * 	// Create a vector with an ever increasing length
	 * 	// and an angle in increments of 45 degrees
	 * 	var vector = new Point({
	 * 	    angle: i * 45,
	 * 	    length: i / 2
	 * 	});
	 * 	// Add the vector relatively to the last segment point:
	 * 	path.lineBy(vector);
	 * }
	 *
	 * // Smooth the handles of the path:
	 * path.smooth();
	 *
	 * // Uncomment the following line and click on 'run' to see
	 * // the construction of the path:
	 * // path.selected = true;
	 */

	// DOCS: Document Path#curveBy
	/**
	 * @name PathItem#curveBy
	 * @function
	 * @param {Point} throughVector
	 * @param {Point} toVector
	 * @param {Number} [parameter=0.5]
	 */

	// DOCS: Document Path#arcBy
	/**
	 * @name PathItem#arcBy
	 * @function
	 * @param {Point} throughVector
	 * @param {Point} toVector
	 */
});
<commit_msg>Path#smooth() example: select path using fullySelected instead of selected, so we can see its handles.<commit_after>/*
 * Paper.js
 *
 * This file is part of Paper.js, a JavaScript Vector Graphics Library,
 * based on Scriptographer.org and designed to be largely API compatible.
 * http://paperjs.org/
 * http://scriptographer.org/
 *
 * Copyright (c) 2011, Juerg Lehni & Jonathan Puckey
 * http://lehni.org/ & http://jonathanpuckey.com/
 *
 * Distributed under the MIT license. See LICENSE file for details.
 *
 * All rights reserved.
 */

/**
 * @name PathItem
 *
 * @class The PathItem class is the base for any items that describe paths
 * and offer standardised methods for drawing and path manipulation, such as
 * {@link Path} and {@link CompoundPath}.
 *
 * @extends Item
 */
var PathItem = this.PathItem = Item.extend(/** @lends PathItem# */{
	// Note: The PathItem class is currently empty but holds the documentation
	// for all the methods that exist both on Path and CompoundPath.
	/**
	 * Smooth bezier curves without changing the amount of segments or their
	 * points, by only smoothing and adjusting their handle points, for both
	 * open ended and closed paths.
	 *
	 * @name PathItem#smooth
	 * @function
	 *
	 * @example {@paperscript}
	 * // Smoothing a closed shape:
	 *
	 * // Create a rectangular path with its top-left point at
	 * // {x: 30, y: 25} and a size of {width: 50, height: 50}:
	 * var path = new Path.Rectangle(new Point(30, 25), new Size(50, 50));
	 * path.strokeColor = 'black';
	 *
	 * // Select the path, so we can see its handles:
	 * path.fullySelected = true;
	 *
	 * // Create a copy of the path and move it 100pt to the right:
	 * var copy = path.clone();
	 * copy.position.x += 100;
	 *
	 * // Smooth the segments of the copy:
	 * copy.smooth();
	 *
	 * @example {@paperscript height=220}
	 * var path = new Path();
	 * path.strokeColor = 'black';
	 *
	 * path.add(new Point(30, 50));
	 *
	 * var y = 5;
	 * var x = 3;
	 *
	 * for (var i = 0; i < 28; i++) {
	 *     y *= -1.1;
	 *     x *= 1.1;
	 *     path.lineBy(x, y);
	 * }
	 *
	 * // Create a copy of the path and move it 100pt down:
	 * var copy = path.clone();
	 * copy.position.y += 120;
	 *
	 * // Set its stroke color to red:
	 * copy.strokeColor = 'red';
	 *
	 * // Smooth the segments of the copy:
	 * copy.smooth();
	 */

	/**
	 * {@grouptitle Postscript Style Drawing Commands}
	 *
	 * If called on a {@link CompoundPath}, a new {@link Path} is created as a
	 * child and the point is added as its first segment. On a normal empty
	 * {@link Path}, the point is simply added as its first segment.
	 *
	 * @name PathItem#moveTo
	 * @function
	 * @param {Point} point
	 */

	// DOCS: Document lineTo
	/**
	 * @name PathItem#lineTo
	 * @function
	 * @param {Point} point
	 */

	/**
	 * Adds a cubic bezier curve to the path, defined by two handles and a to
	 * point.
	 *
	 * @name PathItem#cubicCurveTo
	 * @function
	 * @param {Point} handle1
	 * @param {Point} handle2
	 * @param {Point} to
	 */

	/**
	 * Adds a quadratic bezier curve to the path, defined by a handle and a to
	 * point.
	 *
	 * @name PathItem#quadraticCurveTo
	 * @function
	 * @param {Point} handle
	 * @param {Point} to
	 */

	// DOCS: Document PathItem#curveTo 'paramater' param.
	/**
	 * Draws a curve from the position of the last segment point in the path
	 * that goes through the specified {@code through} point, to the specified
	 * {@code to} point by adding one segment to the path.
	 *
	 * @name PathItem#curveTo
	 * @function
	 * @param {Point} through the point through which the curve should go
	 * @param {Point} to the point where the curve should end
	 * @param {Number} [parameter=0.5]
	 *
	 * @example {@paperscript height=300}
	 * // Interactive example. Click and drag in the view below:
	 *
	 * var myPath;
	 * function onMouseDrag(event) {
	 * 	// If we created a path before, remove it:
	 * 	if (myPath) {
	 * 	    myPath.remove();
	 * 	}
	 *
	 * 	// Create a new path and add a segment point to it
	 * 	// at {x: 150, y: 150):
	 * 	myPath = new Path();
	 * 	myPath.add(150, 150);
	 *
	 * 	// Draw a curve through the position of the mouse to 'toPoint'
	 * 	var toPoint = new Point(350, 150);
	 * 	myPath.curveTo(event.point, toPoint);
	 *
	 * 	// Select the path, so we can see its segments:
	 * 	myPath.selected = true;
	 * }
     *
	 * // When the mouse is released, deselect the path
	 * // and set its stroke-color to black:
	 * function onMouseUp(event) {
	 * 	myPath.selected = false;
	 * 	myPath.strokeColor = 'black';
	 * }
	 */

	/**
	 * Draws an arc from the position of the last segment point in the path that
	 * goes through the specified {@code through} point, to the specified
	 * {@code to} point by adding one or more segments to the path.
	 *
	 * @name PathItem#arcTo
	 * @function
	 * @param {Point} through the point where the arc should pass through
	 * @param {Point} to the point where the arc should end
	 *
	 * @example {@paperscript}
	 * var path = new Path();
	 * path.strokeColor = 'black';
	 *
	 * var firstPoint = new Point(30, 75);
	 * path.add(firstPoint);
	 *
	 * // The point through which we will create the arc:
	 * var throughPoint = new Point(40, 40);
	 *
	 * // The point at which the arc will end:
	 * var toPoint = new Point(130, 75);
	 *
	 * // Draw an arc through 'throughPoint' to 'toPoint'
	 * path.arcTo(throughPoint, toPoint);
	 *
	 * // Add a red circle shaped path at the position of 'throughPoint':
	 * var circle = new Path.Circle(throughPoint, 3);
	 * circle.fillColor = 'red';
	 *
	 * @example {@paperscript height=300}
	 * // Interactive example. Click and drag in the view below:
	 *
	 * var myPath;
	 * function onMouseDrag(event) {
	 * 	// If we created a path before, remove it:
	 * 	if (myPath) {
	 * 	    myPath.remove();
	 * 	}
	 *
	 * 	// Create a new path and add a segment point to it
	 * 	// at {x: 150, y: 150):
	 * 	myPath = new Path();
	 * 	myPath.add(150, 150);
	 *
	 * 	// Draw an arc through the position of the mouse to 'toPoint'
	 * 	var toPoint = new Point(350, 150);
	 * 	myPath.arcTo(event.point, toPoint);
	 *
	 * 	// Select the path, so we can see its segments:
	 * 	myPath.selected = true;
	 * }
	 *
	 * // When the mouse is released, deselect the path
	 * // and fill it with black.
	 * function onMouseUp(event) {
	 * 	myPath.selected = false;
	 * 	myPath.fillColor = 'black';
	 * }
	 */
	/**
	 * Draws an arc from the position of the last segment point in the path to
	 * the specified point by adding one or more segments to the path.
	 *
	 * @name PathItem#arcTo
	 * @function
	 * @param {Point} to the point where the arc should end
	 * @param {Boolean} [clockwise=true] specifies whether the arc should be
	 *        drawn in clockwise direction.
	 *
	 * @example {@paperscript}
	 * var path = new Path();
	 * path.strokeColor = 'black';
	 *
	 * path.add(new Point(30, 75));
	 * path.arcTo(new Point(130, 75));
	 *
	 * var path2 = new Path();
	 * path2.strokeColor = 'red';
	 * path2.add(new Point(180, 25));
	 *
	 * // To draw an arc in anticlockwise direction,
	 * // we pass 'false' as the second argument to arcTo:
	 * path2.arcTo(new Point(280, 25), false);
	 *
	 * @example {@paperscript height=300}
	 * // Interactive example. Click and drag in the view below:
	 * var myPath;
	 *
	 * // The mouse has to move at least 20 points before
	 * // the next mouse drag event is fired:
	 * tool.minDistance = 20;
	 *
	 * // When the user clicks, create a new path and add
	 * // the current mouse position to it as its first segment:
	 * function onMouseDown(event) {
	 * 	myPath = new Path();
	 * 	myPath.strokeColor = 'black';
	 * 	myPath.add(event.point);
	 * }
	 *
	 * // On each mouse drag event, draw an arc to the current
	 * // position of the mouse:
	 * function onMouseDrag(event) {
	 * 	myPath.arcTo(event.point);
	 * }
	 */

	/**
	 * Closes the path. When closed, Paper.js connects the first and last
	 * segments.
	 *
	 * @name PathItem#closePath
	 * @function
	 * @see Path#closed
	 */

	/**
	 * {@grouptitle Relative Drawing Commands}
	 *
	 * If called on a {@link CompoundPath}, a new {@link Path} is created as a
	 * child and the point is added as its first segment relative to the
	 * position of the last segment of the current path.
	 *
	 * @name PathItem#moveBy
	 * @function
	 * @param {Point} point
	 */

	/**
	 * Adds a segment relative to the last segment point of the path.
	 *
	 * @name PathItem#lineBy
	 * @function
	 * @param {Point} vector The vector which is added to the position of the
	 *        last segment of the path, to become the new segment.
	 *
	 * @example {@paperscript}
	 * var path = new Path();
	 * path.strokeColor = 'black';
	 *
	 * // Add a segment at {x: 50, y: 50}
	 * path.add(25, 25);
	 *
	 * // Add a segment relative to the last segment of the path.
	 * // 50 in x direction and 0 in y direction, becomes {x: 75, y: 25}
	 * path.lineBy(50, 0);
	 *
	 * // 0 in x direction and 50 in y direction, becomes {x: 75, y: 75}
	 * path.lineBy(0, 50);
	 *
	 * @example {@paperscript height=300}
	 * // Drawing a spiral using lineBy:
	 * var path = new Path();
	 * path.strokeColor = 'black';
	 *
	 * // Add the first segment at {x: 50, y: 50}
	 * path.add(view.center);
	 *
	 * // Loop 500 times:
	 * for (var i = 0; i < 500; i++) {
	 * 	// Create a vector with an ever increasing length
	 * 	// and an angle in increments of 45 degrees
	 * 	var vector = new Point({
	 * 	    angle: i * 45,
	 * 	    length: i / 2
	 * 	});
	 * 	// Add the vector relatively to the last segment point:
	 * 	path.lineBy(vector);
	 * }
	 *
	 * // Smooth the handles of the path:
	 * path.smooth();
	 *
	 * // Uncomment the following line and click on 'run' to see
	 * // the construction of the path:
	 * // path.selected = true;
	 */

	// DOCS: Document Path#curveBy
	/**
	 * @name PathItem#curveBy
	 * @function
	 * @param {Point} throughVector
	 * @param {Point} toVector
	 * @param {Number} [parameter=0.5]
	 */

	// DOCS: Document Path#arcBy
	/**
	 * @name PathItem#arcBy
	 * @function
	 * @param {Point} throughVector
	 * @param {Point} toVector
	 */
});
<|endoftext|>


<commit_before>function getStorageValue(item, default_value){
    var retVal = localStorage.getItem(item);
    //console.log('value' + item + ': ' + String(retVal));
    if (retVal === null || retVal == 'undefined' || retVal == 'null'){
        retVal = default_value;
    }
    return retVal;
}

/**
 * Overwrites obj1's values with obj2's and adds obj2's if non existent in obj1
 * @param obj1
 * @param obj2
 * @returns obj3 a new object based on obj1 and obj2
 * Similar to ES6 Object.assign() but without side effects and works with older js implementations
 * See
 *    * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
 *    * http://stackoverflow.com/questions/171251/how-can-i-merge-properties-of-two-javascript-objects-dynamically
 */
function merge_options(obj1, obj2){
    var obj3 = {};
    var attrname;

    for (attrname in obj1) { obj3[attrname] = obj1[attrname]; }
    for (attrname in obj2) { obj3[attrname] = obj2[attrname]; }
    return obj3;
}

Pebble.addEventListener('showConfiguration', function(e) {
    /* DEBUG test old settings are handled and cleaned up */
    /*
            localStorage.setItem('background_color', '0000FF');
            localStorage.setItem('time_color', 'FFFF00');
            localStorage.setItem('vibrate_disconnect', true);
    */
    /* DEBUG */

    var URL = 'http://clach04.github.io/pebble/watchface_framework/slate/index.html';

    var default_dict = {
        version_settings: 0,  // Bump this when ever config storage changes
        // http://developer.getpebble.com/tools/color-picker/
        background_color: '000000',  // GColorBlack
        time_color: 'FFFFFF',  // GColorWhite
        vibrate_disconnect: 0
    };
    var stored_dict_str = getStorageValue('stored_dict', null);
    console.log('stored_dict_str: ' + stored_dict_str);
    var stored_dict;
    if (stored_dict_str === null)
    {
        stored_dict = {};
    }
    else
    {
        stored_dict = JSON.parse(stored_dict_str);
    }
    console.log('stored_dict: ' + JSON.stringify(stored_dict));
    console.log('default_dict: ' + JSON.stringify(default_dict));

    /* Handle old config - not stored in dict */
    console.log('Checking old (non-dict) config items');
    var old_config_names = ['background_color', 'time_color', 'vibrate_disconnect'];
    for (var i in old_config_names)
    {
        var config_name=old_config_names[i];
        var tmp_setting;
        console.log('i=' + i + '=' + config_name);
        tmp_setting = getStorageValue(config_name, null);
        if (tmp_setting !== null)
        {
            default_dict[config_name] = tmp_setting;
            console.log('about to remove old localStorage item ' + config_name);
            localStorage.removeItem(config_name);
        }
    }

    var configuration = merge_options(default_dict, stored_dict);
    console.log('configuration: ' + JSON.stringify(configuration));

    var param_array = [];
    var temp_str;
    var config_key;
    for (config_key in configuration)
    {
        console.log('config_key: ' + config_key + '=' + configuration[config_key]);
        temp_str = config_key + '=' + encodeURIComponent(configuration[config_key]);
        param_array.push(temp_str);
    }
    var URL_with_params = URL + '?' + param_array.join('&');
    console.log('Configuration window opened. ' + URL_with_params);
    Pebble.openURL(URL_with_params);
});

Pebble.addEventListener('webviewclosed',
    function(e) {
        console.log('e.response: ' + e.response);
        console.log('e.response.length: ' + e.response.length);
        try {
            var configuration = JSON.parse(decodeURIComponent(e.response));
            var vibrate_disconnect = 0;

            console.log('dictionary to validate ' + JSON.stringify(configuration));
            /*
            ** configuration can from an external web site, this is not trustworthy
            ** and so needs validation. This avoids needing to add lots of
            ** protection to the C code on the Pebble.
            */
            // TODO as configuration is untrusted and may be missing values, merge in from default_dict

            if ('vibrate_disconnect' in configuration)
            {
                switch (configuration.vibrate_disconnect) {
                    case true:
                    case 'true':
                    case 'True':
                    case 'TRUE':
                    case 1:
                    case '1':
                    case 'on':
                        vibrate_disconnect = 1;
                        break;
                    default:
                        vibrate_disconnect = 0;
                        break;
                }
            }
            var dictionary = {
              "KEY_TIME_COLOR": parseInt(configuration.time_color, 16),
              "KEY_BACKGROUND_COLOR": parseInt(configuration.background_color, 16), // FIXME if mising default value..
              "KEY_VIBRATE_ON_DISCONNECT": vibrate_disconnect
            };
            /* even though we don't realy trust `configuration`, store it in local phone storage */
            // TODO store dictionary instead - which stores number differently
            console.log('store config on phone');
            localStorage.setItem('stored_dict', JSON.stringify(configuration));

            console.log('dictionary to send to Pebble' + JSON.stringify(dictionary));
            // Send to Pebble
            Pebble.sendAppMessage(dictionary,
                function(e) {
                    console.log("Configuration sent to Pebble successfully!");
                },
                function(e) {
                    console.log("Error sending configuration info to Pebble!");
                }
            );
        } catch (ex) {
            // If we have SyntaxError JSON is invalid, anything unknown!?
            if (ex instanceof SyntaxError) {
                console.log('Probably Cancelled');
            } else {
                throw ex;
                // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SyntaxError
            }
        }
    }
);
<commit_msg>Refactor move pebble config code to same location<commit_after>function getStorageValue(item, default_value){
    var retVal = localStorage.getItem(item);
    //console.log('value' + item + ': ' + String(retVal));
    if (retVal === null || retVal == 'undefined' || retVal == 'null'){
        retVal = default_value;
    }
    return retVal;
}

/**
 * Overwrites obj1's values with obj2's and adds obj2's if non existent in obj1
 * @param obj1
 * @param obj2
 * @returns obj3 a new object based on obj1 and obj2
 * Similar to ES6 Object.assign() but without side effects and works with older js implementations
 * See
 *    * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
 *    * http://stackoverflow.com/questions/171251/how-can-i-merge-properties-of-two-javascript-objects-dynamically
 */
function merge_options(obj1, obj2){
    var obj3 = {};
    var attrname;

    for (attrname in obj1) { obj3[attrname] = obj1[attrname]; }
    for (attrname in obj2) { obj3[attrname] = obj2[attrname]; }
    return obj3;
}

Pebble.addEventListener('showConfiguration', function(e) {
    /* DEBUG test old settings are handled and cleaned up */
    /*
            localStorage.setItem('background_color', '0000FF');
            localStorage.setItem('time_color', 'FFFF00');
            localStorage.setItem('vibrate_disconnect', true);
    */
    /* DEBUG */

    var URL = 'http://clach04.github.io/pebble/watchface_framework/slate/index.html';

    var default_dict = {
        version_settings: 0,  // Bump this when ever config storage changes
        // http://developer.getpebble.com/tools/color-picker/
        background_color: '000000',  // GColorBlack
        time_color: 'FFFFFF',  // GColorWhite
        vibrate_disconnect: 0
    };
    var stored_dict_str = getStorageValue('stored_dict', null);
    console.log('stored_dict_str: ' + stored_dict_str);
    var stored_dict;
    if (stored_dict_str === null)
    {
        stored_dict = {};
    }
    else
    {
        stored_dict = JSON.parse(stored_dict_str);
    }
    console.log('stored_dict: ' + JSON.stringify(stored_dict));
    console.log('default_dict: ' + JSON.stringify(default_dict));

    /* Handle old config - not stored in dict */
    console.log('Checking old (non-dict) config items');
    var old_config_names = ['background_color', 'time_color', 'vibrate_disconnect'];
    for (var i in old_config_names)
    {
        var config_name=old_config_names[i];
        var tmp_setting;
        console.log('i=' + i + '=' + config_name);
        tmp_setting = getStorageValue(config_name, null);
        if (tmp_setting !== null)
        {
            default_dict[config_name] = tmp_setting;
            console.log('about to remove old localStorage item ' + config_name);
            localStorage.removeItem(config_name);
        }
    }

    var configuration = merge_options(default_dict, stored_dict);
    console.log('configuration: ' + JSON.stringify(configuration));

    var param_array = [];
    var temp_str;
    var config_key;
    for (config_key in configuration)
    {
        console.log('config_key: ' + config_key + '=' + configuration[config_key]);
        temp_str = config_key + '=' + encodeURIComponent(configuration[config_key]);
        param_array.push(temp_str);
    }
    var URL_with_params = URL + '?' + param_array.join('&');
    console.log('Configuration window opened. ' + URL_with_params);
    Pebble.openURL(URL_with_params);
});

Pebble.addEventListener('webviewclosed',
    function(e) {
        console.log('e.response: ' + e.response);
        console.log('e.response.length: ' + e.response.length);
        try {
            var configuration = JSON.parse(decodeURIComponent(e.response));
            var vibrate_disconnect = 0;

            console.log('dictionary to validate ' + JSON.stringify(configuration));
            /*
            ** configuration can from an external web site, this is not trustworthy
            ** and so needs validation. This avoids needing to add lots of
            ** protection to the C code on the Pebble.
            */
            // TODO as configuration is untrusted and may be missing values, merge in from default_dict

            /* even though we don't realy trust `configuration`, store it in local phone storage */
            // TODO store dictionary instead - which stores number differently
            console.log('store config on phone');
            localStorage.setItem('stored_dict', JSON.stringify(configuration));

            // Send to Pebble
            if ('vibrate_disconnect' in configuration)
            {
                switch (configuration.vibrate_disconnect) {
                    case true:
                    case 'true':
                    case 'True':
                    case 'TRUE':
                    case 1:
                    case '1':
                    case 'on':
                        vibrate_disconnect = 1;
                        break;
                    default:
                        vibrate_disconnect = 0;
                        break;
                }
            }
            var dictionary = {
              "KEY_TIME_COLOR": parseInt(configuration.time_color, 16),
              "KEY_BACKGROUND_COLOR": parseInt(configuration.background_color, 16),
              "KEY_VIBRATE_ON_DISCONNECT": vibrate_disconnect
            };

            console.log('dictionary to send to Pebble' + JSON.stringify(dictionary));
            Pebble.sendAppMessage(dictionary,
                function(e) {
                    console.log("Configuration sent to Pebble successfully!");
                },
                function(e) {
                    console.log("Error sending configuration info to Pebble!");
                }
            );
        } catch (ex) {
            // If we have SyntaxError JSON is invalid, anything unknown!?
            if (ex instanceof SyntaxError) {
                console.log('Probably Cancelled');
            } else {
                throw ex;
                // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SyntaxError
            }
        }
    }
);
<|endoftext|>


<commit_before>var Polyhymnia = Polyhymnia || {};

Polyhymnia.Player = function(element, context) {
  'use strict';

  // Code
  var contents = element.textContent;

  // Music
  var rules = [];
  var music = context;
  var symbols = [];
  music.setAnimCallback(highlightNotes);

  // Elements
  element.innerHTML = Polyhymnia.templates.player;
  var controls =     element.querySelector('.controls');
  var playButton =   element.querySelector('.play');
  var stopButton =   element.querySelector('.stop');
  var paramSlider =  element.querySelector('.slider input');
  var paramOutput =  element.querySelector('.slider .output');
  var tempoInput =   element.querySelector('.tempo input');
  var codeEditor =   element.querySelector('.code .editor');
  var codeDisplay =  element.querySelector('.code .display');
  var codeText =     element.querySelector('.code .text');
  var codeCursor =   element.querySelector('.code .cursor');
  var notSupportedMessage = element.querySelector('.not-supported');
  var noteElems = [];
  codeEditor.value = contents;

  // Private vars
  var cursorPos;

  // Functions
  function play() {
    parse();
    music.play();
    playButton.style.display = 'none';
    stopButton.style.display = 'block';
  }

  function stop() {
    music.stop();
    playButton.style.display = 'block';
    stopButton.style.display = 'none';
    highlightNotes([]);
  }

  function parse() {
    // Parse rules
    var rules = music.parse(codeEditor.value);
    symbols = rules.symbols;

    // Render the code
    renderCode();
  }

  function changeTempo() {
    if (tempoInput.value === '') {
      tempoInput.value = 120;
    } else if (tempoInput.value > parseInt(tempoInput.max)) {
      tempoInput.value = tempoInput.max;
    } else if (tempoInput.value < parseInt(tempoInput.min)) {
      tempoInput.value = tempoInput.min;
    }
    music.setTempo(tempoInput.value);
  }

  function changeParam() {
    // Update the value
    music.setParam('x', paramSlider.valueAsNumber);

    // Show the value
    paramOutput.textContent = 'x = ' + paramSlider.value;
    paramOutput.classList.remove('hide');
    paramOutput.classList.add('show');

    // Move the value with the slider
    var pos = paramSlider.value / (paramSlider.max - paramSlider.min);
    var nudge = 8 * (1 - pos) - 8 * pos; // Offset for handle size 16px
    paramOutput.style.left = pos * paramSlider.offsetWidth - paramOutput.offsetWidth/2 + nudge + 'px';

    // Hide the value again
    setTimeout(function() {
      paramOutput.classList.add('hide');
      paramOutput.classList.remove('show');
    }, 2000);
  }

  function renderCode() {
    // Get the code
    var code = codeEditor.value;

    // Wrap symbols in spans
    var html = '';
    var s = 0;
    for (var i = 0; i < code.length; i++) {
      if (s < symbols.length && symbols[s].start == i) {
        html += '';
      }

      html += code.charAt(i);

      if (s < symbols.length && symbols[s].end == i + 1) {
        html += '';
        s++;
      }
    }

    // Replace contents of the code display
    codeText.innerHTML = html;

    // Get all note elements for later highlighting
    noteElems = [];
    var elems = codeText.querySelectorAll('.note');
    for (var e = 0; e < elems.length; e++) {
      noteElems.push({ elem: elems[e], start: elems[e].dataset.start });
    }
  }

  function render() {
    var newCursorPos = codeEditor.selectionDirection == 'forward' ? codeEditor.selectionEnd : codeEditor.selectionStart;

    // Draw the cursor
    if (document.activeElement === codeEditor) {
      codeCursor.style.display = 'block';

      // Only redraw the cursor if it's moved
      if (newCursorPos !== cursorPos) {
        cursorPos = newCursorPos;

        // Measure text sizes
        var rect = codeCursor.getBoundingClientRect();
        var lineHeight = rect.height - (codeCursor.offsetHeight - codeCursor.clientHeight);
        var characterWidth = rect.width - (codeCursor.offsetWidth - codeCursor.clientWidth);

        // Calculate position
        var lines = codeEditor.value.substr(0, cursorPos).split('\n');
        var top = (lines.length - 1) * lineHeight;
        var left = lines[lines.length - 1].length * characterWidth;

        // Move the cursor
        codeCursor.classList.remove('blink');
        codeCursor.style.top = top + 'px';
        codeCursor.style.left = left - 1 + 'px';
        var reflow = codeCursor.offsetWidth; // Trigger animation reset
        codeCursor.classList.add('blink');
      }
    } else {
      codeCursor.style.display = 'none';
    }

    // Sync scroll
    codeDisplay.scrollTop = codeEditor.scrollTop;

    // Repaint
    window.requestAnimationFrame(render);
  }

  function highlightNotes(notes) {
    for (var i = 0; i < noteElems.length; i++) {
      var highlight = false;
      for (var j = 0; j < notes.length; j++) {
        if (noteElems[i].start == notes[j].start) {
          highlight = true;
        }
      }
      if (highlight) {
        noteElems[i].elem.className = 'playing';
      } else {
        noteElems[i].elem.className = 'note';
      }
    }
  }

  // Events
  if (Polyhymnia.isSupported()) {
    playButton.addEventListener('click',  play);
    stopButton.addEventListener('click',  stop);
    paramSlider.addEventListener('input', changeParam);
    tempoInput.addEventListener('input',  changeTempo);
    codeEditor.addEventListener('input',  parse);
  } else {
    controls.style.display = 'none';
    notSupportedMessage.style.display = 'block';
  }

  // Rendering
  renderCode();
  window.requestAnimationFrame(render);
};<commit_msg>Fix bug where code would not render properly in editor on load<commit_after>var Polyhymnia = Polyhymnia || {};

Polyhymnia.Player = function(element, context) {
  'use strict';

  // Code
  var contents = element.textContent;

  // Music
  var rules = [];
  var music = context;
  var symbols = [];
  music.setAnimCallback(highlightNotes);

  // Elements
  element.innerHTML = Polyhymnia.templates.player;
  var controls =     element.querySelector('.controls');
  var playButton =   element.querySelector('.play');
  var stopButton =   element.querySelector('.stop');
  var paramSlider =  element.querySelector('.slider input');
  var paramOutput =  element.querySelector('.slider .output');
  var tempoInput =   element.querySelector('.tempo input');
  var codeEditor =   element.querySelector('.code .editor');
  var codeDisplay =  element.querySelector('.code .display');
  var codeText =     element.querySelector('.code .text');
  var codeCursor =   element.querySelector('.code .cursor');
  var notSupportedMessage = element.querySelector('.not-supported');
  var noteElems = [];
  codeEditor.value = contents;

  // Private vars
  var cursorPos;

  // Functions
  function play() {
    parse();
    music.play();
    playButton.style.display = 'none';
    stopButton.style.display = 'block';
  }

  function stop() {
    music.stop();
    playButton.style.display = 'block';
    stopButton.style.display = 'none';
    highlightNotes([]);
  }

  function parse() {
    // Parse rules
    var rules = music.parse(codeEditor.value);
    symbols = rules.symbols;

    // Render the code
    renderCode();
  }

  function changeTempo() {
    if (tempoInput.value === '') {
      tempoInput.value = 120;
    } else if (tempoInput.value > parseInt(tempoInput.max)) {
      tempoInput.value = tempoInput.max;
    } else if (tempoInput.value < parseInt(tempoInput.min)) {
      tempoInput.value = tempoInput.min;
    }
    music.setTempo(tempoInput.value);
  }

  function changeParam() {
    // Update the value
    music.setParam('x', paramSlider.valueAsNumber);

    // Show the value
    paramOutput.textContent = 'x = ' + paramSlider.value;
    paramOutput.classList.remove('hide');
    paramOutput.classList.add('show');

    // Move the value with the slider
    var pos = paramSlider.value / (paramSlider.max - paramSlider.min);
    var nudge = 8 * (1 - pos) - 8 * pos; // Offset for handle size 16px
    paramOutput.style.left = pos * paramSlider.offsetWidth - paramOutput.offsetWidth/2 + nudge + 'px';

    // Hide the value again
    setTimeout(function() {
      paramOutput.classList.add('hide');
      paramOutput.classList.remove('show');
    }, 2000);
  }

  function renderCode() {
    // Get the code
    var code = codeEditor.value;

    // Wrap symbols in spans
    var html = '';
    var s = 0;
    for (var i = 0; i < code.length; i++) {
      if (s < symbols.length && symbols[s].start == i) {
        html += '';
      }

      html += code.charAt(i);

      if (s < symbols.length && symbols[s].end == i + 1) {
        html += '';
        s++;
      }
    }

    // Replace contents of the code display
    codeText.innerHTML = html;

    // Get all note elements for later highlighting
    noteElems = [];
    var elems = codeText.querySelectorAll('.note');
    for (var e = 0; e < elems.length; e++) {
      noteElems.push({ elem: elems[e], start: elems[e].dataset.start });
    }
  }

  function render() {
    var newCursorPos = codeEditor.selectionDirection == 'forward' ? codeEditor.selectionEnd : codeEditor.selectionStart;

    // Draw the cursor
    if (document.activeElement === codeEditor) {
      codeCursor.style.display = 'block';

      // Only redraw the cursor if it's moved
      if (newCursorPos !== cursorPos) {
        cursorPos = newCursorPos;

        // Measure text sizes
        var rect = codeCursor.getBoundingClientRect();
        var lineHeight = rect.height - (codeCursor.offsetHeight - codeCursor.clientHeight);
        var characterWidth = rect.width - (codeCursor.offsetWidth - codeCursor.clientWidth);

        // Calculate position
        var lines = codeEditor.value.substr(0, cursorPos).split('\n');
        var top = (lines.length - 1) * lineHeight;
        var left = lines[lines.length - 1].length * characterWidth;

        // Move the cursor
        codeCursor.classList.remove('blink');
        codeCursor.style.top = top + 'px';
        codeCursor.style.left = left - 1 + 'px';
        var reflow = codeCursor.offsetWidth; // Trigger animation reset
        codeCursor.classList.add('blink');
      }
    } else {
      codeCursor.style.display = 'none';
    }

    // Sync scroll
    codeDisplay.scrollTop = codeEditor.scrollTop;

    // Repaint
    window.requestAnimationFrame(render);
  }

  function highlightNotes(notes) {
    for (var i = 0; i < noteElems.length; i++) {
      var highlight = false;
      for (var j = 0; j < notes.length; j++) {
        if (noteElems[i].start == notes[j].start) {
          highlight = true;
        }
      }
      if (highlight) {
        noteElems[i].elem.className = 'playing';
      } else {
        noteElems[i].elem.className = 'note';
      }
    }
  }

  // Events
  if (Polyhymnia.isSupported()) {
    playButton.addEventListener('click',  play);
    stopButton.addEventListener('click',  stop);
    paramSlider.addEventListener('input', changeParam);
    tempoInput.addEventListener('input',  changeTempo);
    codeEditor.addEventListener('input',  parse);
  } else {
    controls.style.display = 'none';
    notSupportedMessage.style.display = 'block';
  }

  // Rendering
  parse();
  window.requestAnimationFrame(render);
};<|endoftext|>


<commit_before>import Plugin from './Plugin'
import tus from 'tus-js-client'
import UppySocket from '../core/UppySocket'

/**
 * Tus resumable file uploader
 *
 */
export default class Tus10 extends Plugin {
  constructor (core, opts) {
    super(core, opts)
    this.type = 'uploader'
    this.id = 'Tus'
    this.title = 'Tus'

    // set default options
    const defaultOptions = {
      resume: true
    }

    // merge default options with the ones set by user
    this.opts = Object.assign({}, defaultOptions, opts)
  }

  pauseUpload (fileID) {
    const updatedFiles = Object.assign({}, this.core.getState().files)
    const wasPaused = updatedFiles[fileID].isPaused || false
    const isPaused = !wasPaused
    let updatedFile
    if (wasPaused) {
      updatedFile = Object.assign({}, updatedFiles[fileID], {
        isPaused: false
      })
    } else {
      updatedFile = Object.assign({}, updatedFiles[fileID], {
        isPaused: true
      })
    }
    updatedFiles[fileID] = updatedFile
    this.core.setState({files: updatedFiles})

    return isPaused
  }

/**
 * Create a new Tus upload
 *
 * @param {object} file for use with upload
 * @param {integer} current file in a queue
 * @param {integer} total number of files in a queue
 * @returns {Promise}
 */
  upload (file, current, total) {
    this.core.log(`uploading ${current} of ${total}`)
    console.log(file.meta)

    // Create a new tus upload
    return new Promise((resolve, reject) => {
      const upload = new tus.Upload(file.data, {

        // TODO merge this.opts or this.opts.tus here
        metadata: {bla: 123, blabla: ''},
        // metadata: file.meta
        // metadata: { name: "IMG_9078.jpg", resizeTo: 1200, description: '' },
        resume: this.opts.resume,
        endpoint: this.opts.endpoint,

        onError: (err) => {
          this.core.log(err)
          reject('Failed because: ' + err)
        },
        onProgress: (bytesUploaded, bytesTotal) => {
          // Dispatch progress event
          this.core.emitter.emit('upload-progress', {
            uploader: this,
            id: file.id,
            bytesUploaded: bytesUploaded,
            bytesTotal: bytesTotal
          })
        },
        onSuccess: () => {
          this.core.emitter.emit('upload-success', file.id, upload.url)

          this.core.log(`Download ${upload.file.name} from ${upload.url}`)
          resolve(upload)
        }
      })

      this.core.emitter.on('file-remove', (fileID) => {
        if (fileID === file.id) {
          upload.abort()
        }
      })

      this.core.emitter.on('core:upload-pause', (fileID) => {
        if (fileID === file.id) {
          const isPaused = this.pauseUpload(fileID)
          isPaused ? upload.abort() : upload.start()
        }
      })

      window.upload = upload

      upload.start()
    })
  }

  uploadRemote (file, current, total) {
    return new Promise((resolve, reject) => {
      this.core.log(file.remote.url)
      fetch(file.remote.url, {
        method: 'post',
        credentials: 'include',
        headers: {
          'Accept': 'application/json',
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(Object.assign({}, file.remote.body, {
          target: this.opts.endpoint,
          protocol: 'tus'
        }))
      })
      .then((res) => {
        if (res.status < 200 && res.status > 300) {
          return reject(res.statusText)
        }

        res.json()
        .then((data) => {
          // get the host domain
          var regex = /^(?:https?:\/\/)?(?:[^@\/\n]+@)?(?:www\.)?([^:\/\n]+)/
          var host = regex.exec(file.remote.host)[1]

          var token = data.token
          var socket = new UppySocket({
            target: `ws://${host}:3121/api/${token}`
          })

          socket.on('progress', (progressData) => {
            if (progressData.complete) {
              this.core.log(`Remote upload of '${file.name}' successful`)
              this.core.emitter.emit('upload-success', file)
              return resolve('Success')
            }

            if (progressData.progress) {
              this.core.log(`Upload progress: ${progressData.progress}`)

              // Dispatch progress event
              this.core.emitter.emit('upload-progress', {
                uploader: this,
                id: file.id,
                bytesUploaded: progressData.bytesUploaded,
                bytesTotal: progressData.bytesTotal
              })
            }
          })
        })
      })
    })
  }

  uploadFiles (files) {
    const uploaders = []
    files.forEach((file, index) => {
      const current = parseInt(index, 10) + 1
      const total = files.length

      if (!file.isRemote) {
        uploaders.push(this.upload(file, current, total))
      } else {
        uploaders.push(this.uploadRemote(file, current, total))
      }
    })

    return Promise.all(uploaders).then(() => {
      return {
        uploadedCount: files.length
      }
    })
  }

  selectForUpload (files) {
    // TODO: replace files[file].isRemote with some logic
    //
    // filter files that are now yet being uploaded / haven’t been uploaded
    // and remote too
    const filesForUpload = Object.keys(files).filter((file) => {
      if (files[file].progress === 0 || files[file].isRemote) {
        return true
      }
      return false
    }).map((file) => {
      return files[file]
    })

    this.uploadFiles(filesForUpload)
  }

  install () {
    this.core.emitter.on('next', () => {
      this.core.log('Tus is uploading...')
      const files = this.core.getState().files
      this.selectForUpload(files)
    })
  }
}
<commit_msg>use Uppy’s metadata again<commit_after>import Plugin from './Plugin'
import tus from 'tus-js-client'
import UppySocket from '../core/UppySocket'

/**
 * Tus resumable file uploader
 *
 */
export default class Tus10 extends Plugin {
  constructor (core, opts) {
    super(core, opts)
    this.type = 'uploader'
    this.id = 'Tus'
    this.title = 'Tus'

    // set default options
    const defaultOptions = {
      resume: true
    }

    // merge default options with the ones set by user
    this.opts = Object.assign({}, defaultOptions, opts)
  }

  pauseUpload (fileID) {
    const updatedFiles = Object.assign({}, this.core.getState().files)
    const wasPaused = updatedFiles[fileID].isPaused || false
    const isPaused = !wasPaused
    let updatedFile
    if (wasPaused) {
      updatedFile = Object.assign({}, updatedFiles[fileID], {
        isPaused: false
      })
    } else {
      updatedFile = Object.assign({}, updatedFiles[fileID], {
        isPaused: true
      })
    }
    updatedFiles[fileID] = updatedFile
    this.core.setState({files: updatedFiles})

    return isPaused
  }

/**
 * Create a new Tus upload
 *
 * @param {object} file for use with upload
 * @param {integer} current file in a queue
 * @param {integer} total number of files in a queue
 * @returns {Promise}
 */
  upload (file, current, total) {
    this.core.log(`uploading ${current} of ${total}`)
    console.log(file.meta)

    // Create a new tus upload
    return new Promise((resolve, reject) => {
      const upload = new tus.Upload(file.data, {

        // TODO merge this.opts or this.opts.tus here
        metadata: file.meta,
        resume: this.opts.resume,
        endpoint: this.opts.endpoint,

        onError: (err) => {
          this.core.log(err)
          reject('Failed because: ' + err)
        },
        onProgress: (bytesUploaded, bytesTotal) => {
          // Dispatch progress event
          this.core.emitter.emit('upload-progress', {
            uploader: this,
            id: file.id,
            bytesUploaded: bytesUploaded,
            bytesTotal: bytesTotal
          })
        },
        onSuccess: () => {
          this.core.emitter.emit('upload-success', file.id, upload.url)

          this.core.log(`Download ${upload.file.name} from ${upload.url}`)
          resolve(upload)
        }
      })

      this.core.emitter.on('file-remove', (fileID) => {
        if (fileID === file.id) {
          upload.abort()
        }
      })

      this.core.emitter.on('core:upload-pause', (fileID) => {
        if (fileID === file.id) {
          const isPaused = this.pauseUpload(fileID)
          isPaused ? upload.abort() : upload.start()
        }
      })

      window.upload = upload

      upload.start()
    })
  }

  uploadRemote (file, current, total) {
    return new Promise((resolve, reject) => {
      this.core.log(file.remote.url)
      fetch(file.remote.url, {
        method: 'post',
        credentials: 'include',
        headers: {
          'Accept': 'application/json',
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(Object.assign({}, file.remote.body, {
          target: this.opts.endpoint,
          protocol: 'tus'
        }))
      })
      .then((res) => {
        if (res.status < 200 && res.status > 300) {
          return reject(res.statusText)
        }

        res.json()
        .then((data) => {
          // get the host domain
          var regex = /^(?:https?:\/\/)?(?:[^@\/\n]+@)?(?:www\.)?([^:\/\n]+)/
          var host = regex.exec(file.remote.host)[1]

          var token = data.token
          var socket = new UppySocket({
            target: `ws://${host}:3121/api/${token}`
          })

          socket.on('progress', (progressData) => {
            if (progressData.complete) {
              this.core.log(`Remote upload of '${file.name}' successful`)
              this.core.emitter.emit('upload-success', file)
              return resolve('Success')
            }

            if (progressData.progress) {
              this.core.log(`Upload progress: ${progressData.progress}`)

              // Dispatch progress event
              this.core.emitter.emit('upload-progress', {
                uploader: this,
                id: file.id,
                bytesUploaded: progressData.bytesUploaded,
                bytesTotal: progressData.bytesTotal
              })
            }
          })
        })
      })
    })
  }

  uploadFiles (files) {
    const uploaders = []
    files.forEach((file, index) => {
      const current = parseInt(index, 10) + 1
      const total = files.length

      if (!file.isRemote) {
        uploaders.push(this.upload(file, current, total))
      } else {
        uploaders.push(this.uploadRemote(file, current, total))
      }
    })

    return Promise.all(uploaders).then(() => {
      return {
        uploadedCount: files.length
      }
    })
  }

  selectForUpload (files) {
    // TODO: replace files[file].isRemote with some logic
    //
    // filter files that are now yet being uploaded / haven’t been uploaded
    // and remote too
    const filesForUpload = Object.keys(files).filter((file) => {
      if (files[file].progress === 0 || files[file].isRemote) {
        return true
      }
      return false
    }).map((file) => {
      return files[file]
    })

    this.uploadFiles(filesForUpload)
  }

  install () {
    this.core.emitter.on('next', () => {
      this.core.log('Tus is uploading...')
      const files = this.core.getState().files
      this.selectForUpload(files)
    })
  }
}
<|endoftext|>