<commit_before>/*
 * JavaScript tracker for Snowplow: proxies.js
 *
 * Significant portions copyright 2010 Anthon Pang. Remainder copyright
 * 2012-2014 Snowplow Analytics Ltd. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * * Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 *
 * * Neither the name of Anthon Pang nor Snowplow Analytics Ltd nor the
 *   names of their contributors may be used to endorse or promote products
 *   derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

;(function(){

	var
		helpers = require('./helpers'),
		object = typeof exports !== 'undefined' ? exports : this;

	/*
	 * Fix-up URL when page rendered from search engine cache or translated page.
	 * TODO: it would be nice to generalise this and/or move into the ETL phase.
	 */
	object.fixupUrl = function (hostName, href, referrer) {
		/*
		 * Extract parameter from URL
		 */
		function getParameter(url, name) {
			// scheme : // [username [: password] @] hostname [: port] [/ [path] [? query] [# fragment]]
			var e = new RegExp('^(?:https?|ftp)(?::/*(?:[^?]+)[?])([^#]+)'),
				matches = e.exec(url),
				f = new RegExp('(?:^|&)' + name + '=([^&]*)'),
				result = matches ? f.exec(matches[1]) : 0;
console.log('matches:', matches)
			return result ? decodeURIComponent(result[1]) : '';
		}

		if (hostName === 'translate.googleusercontent.com') {		// Google
			if (referrer === '') {
				referrer = href;
			}
			href = getParameter(href, 'u');
			hostName = helpers.getHostName(href);
		} else if (hostName === 'cc.bingj.com' ||					// Bing
				hostName === 'webcache.googleusercontent.com' ||	// Google
				hostName.slice(0, 5) === '74.6.') {					// Yahoo (via Inktomi 74.6.0.0/16)
			href = document.links[0].href;
			hostName = helpers.getHostName(href);
		}
		return [hostName, href, referrer];
	}

}());
<commit_msg>Removed debug code from proxies.js<commit_after>/*
 * JavaScript tracker for Snowplow: proxies.js
 *
 * Significant portions copyright 2010 Anthon Pang. Remainder copyright
 * 2012-2014 Snowplow Analytics Ltd. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * * Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 *
 * * Neither the name of Anthon Pang nor Snowplow Analytics Ltd nor the
 *   names of their contributors may be used to endorse or promote products
 *   derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

;(function(){

	var
		helpers = require('./helpers'),
		object = typeof exports !== 'undefined' ? exports : this;

	/*
	 * Fix-up URL when page rendered from search engine cache or translated page.
	 * TODO: it would be nice to generalise this and/or move into the ETL phase.
	 */
	object.fixupUrl = function (hostName, href, referrer) {
		/*
		 * Extract parameter from URL
		 */
		function getParameter(url, name) {
			// scheme : // [username [: password] @] hostname [: port] [/ [path] [? query] [# fragment]]
			var e = new RegExp('^(?:https?|ftp)(?::/*(?:[^?]+)[?])([^#]+)'),
				matches = e.exec(url),
				f = new RegExp('(?:^|&)' + name + '=([^&]*)'),
				result = matches ? f.exec(matches[1]) : 0;

			return result ? decodeURIComponent(result[1]) : '';
		}

		if (hostName === 'translate.googleusercontent.com') {		// Google
			if (referrer === '') {
				referrer = href;
			}
			href = getParameter(href, 'u');
			hostName = helpers.getHostName(href);
		} else if (hostName === 'cc.bingj.com' ||					// Bing
				hostName === 'webcache.googleusercontent.com' ||	// Google
				hostName.slice(0, 5) === '74.6.') {					// Yahoo (via Inktomi 74.6.0.0/16)
			href = document.links[0].href;
			hostName = helpers.getHostName(href);
		}
		return [hostName, href, referrer];
	}

}());
<|endoftext|>


<commit_before>import {
    $,
    addClass,
    append,
    apply,
    attr,
    css,
    endsWith,
    includes,
    isFocusable,
    last,
    on,
    once,
    parent,
    pointerCancel,
    pointerDown,
    pointerUp,
    removeClass,
    scrollParents,
    toFloat,
    width,
    within,
} from 'uikit-util';
import Class from './class';
import Container from './container';
import Togglable from './togglable';

const active = [];

export default {
    mixins: [Class, Container, Togglable],

    props: {
        selPanel: String,
        selClose: String,
        escClose: Boolean,
        bgClose: Boolean,
        stack: Boolean,
    },

    data: {
        cls: 'uk-open',
        escClose: true,
        bgClose: true,
        overlay: true,
        stack: false,
    },

    computed: {
        panel({ selPanel }, $el) {
            return $(selPanel, $el);
        },

        transitionElement() {
            return this.panel;
        },

        bgClose({ bgClose }) {
            return bgClose && this.panel;
        },
    },

    beforeDisconnect() {
        if (includes(active, this)) {
            this.toggleElement(this.$el, false, false);
        }
    },

    events: [
        {
            name: 'click',

            delegate() {
                return this.selClose;
            },

            handler(e) {
                e.preventDefault();
                this.hide();
            },
        },

        {
            name: 'toggle',

            self: true,

            handler(e) {
                if (e.defaultPrevented) {
                    return;
                }

                e.preventDefault();

                if (this.isToggled() === includes(active, this)) {
                    this.toggle();
                }
            },
        },

        {
            name: 'beforeshow',

            self: true,

            handler(e) {
                if (includes(active, this)) {
                    return false;
                }

                if (!this.stack && active.length) {
                    Promise.all(active.map((modal) => modal.hide())).then(this.show);
                    e.preventDefault();
                } else {
                    active.push(this);
                }
            },
        },

        {
            name: 'show',

            self: true,

            handler() {
                once(
                    this.$el,
                    'hide',
                    on(document, 'focusin', (e) => {
                        if (last(active) === this && !within(e.target, this.$el)) {
                            this.$el.focus();
                        }
                    })
                );

                if (this.overlay) {
                    once(this.$el, 'hide', preventOverscroll(this.$el));
                    once(this.$el, 'hide', preventBackgroundScroll());
                }

                if (this.stack) {
                    css(this.$el, 'zIndex', toFloat(css(this.$el, 'zIndex')) + active.length);
                }

                addClass(document.documentElement, this.clsPage);

                if (this.bgClose) {
                    once(
                        this.$el,
                        'hide',
                        on(document, pointerDown, ({ target }) => {
                            if (
                                last(active) !== this ||
                                (this.overlay && !within(target, this.$el)) ||
                                within(target, this.panel)
                            ) {
                                return;
                            }

                            once(
                                document,
                                `${pointerUp} ${pointerCancel} scroll`,
                                ({ defaultPrevented, type, target: newTarget }) => {
                                    if (
                                        !defaultPrevented &&
                                        type === pointerUp &&
                                        target === newTarget
                                    ) {
                                        this.hide();
                                    }
                                },
                                true
                            );
                        }),
                        { self: true }
                    );
                }

                if (this.escClose) {
                    once(
                        this.$el,
                        'hide',
                        on(document, 'keydown', (e) => {
                            if (e.keyCode === 27 && last(active) === this) {
                                this.hide();
                            }
                        }),
                        { self: true }
                    );
                }
            },
        },

        {
            name: 'shown',

            self: true,

            handler() {
                if (!isFocusable(this.$el)) {
                    attr(this.$el, 'tabindex', '-1');
                }

                if (!$(':focus', this.$el)) {
                    this.$el.focus();
                }
            },
        },

        {
            name: 'hidden',

            self: true,

            handler() {
                if (includes(active, this)) {
                    active.splice(active.indexOf(this), 1);
                }

                if (!active.length) {
                    css(document.body, 'overflowY', '');
                }

                css(this.$el, 'zIndex', '');

                if (!active.some((modal) => modal.clsPage === this.clsPage)) {
                    removeClass(document.documentElement, this.clsPage);
                }
            },
        },
    ],

    methods: {
        toggle() {
            return this.isToggled() ? this.hide() : this.show();
        },

        show() {
            if (this.container && parent(this.$el) !== this.container) {
                append(this.container, this.$el);
                return new Promise((resolve) =>
                    requestAnimationFrame(() => this.show().then(resolve))
                );
            }

            return this.toggleElement(this.$el, true, animate(this));
        },

        hide() {
            return this.toggleElement(this.$el, false, animate(this));
        },
    },
};

function animate({ transitionElement, _toggle }) {
    return (el, show) =>
        new Promise((resolve, reject) =>
            once(el, 'show hide', () => {
                el._reject?.();
                el._reject = reject;

                _toggle(el, show);

                const off = once(
                    transitionElement,
                    'transitionstart',
                    () => {
                        once(transitionElement, 'transitionend transitioncancel', resolve, {
                            self: true,
                        });
                        clearTimeout(timer);
                    },
                    { self: true }
                );

                const timer = setTimeout(() => {
                    off();
                    resolve();
                }, toMs(css(transitionElement, 'transitionDuration')));
            })
        ).then(() => delete el._reject);
}

function toMs(time) {
    return time ? (endsWith(time, 'ms') ? toFloat(time) : toFloat(time) * 1000) : 0;
}

export function preventOverscroll(el) {
    if (CSS.supports('overscroll-behavior', 'contain')) {
        const elements = filterChildren(el, (child) => /auto|scroll/.test(css(child, 'overflow')));
        css(elements, 'overscrollBehavior', 'contain');
        return () => css(elements, 'overscrollBehavior', '');
    }

    let startClientY;

    const events = [
        on(
            el,
            'touchstart',
            ({ targetTouches }) => {
                if (targetTouches.length === 1) {
                    startClientY = targetTouches[0].clientY;
                }
            },
            { passive: true }
        ),

        on(
            el,
            'touchmove',
            (e) => {
                if (e.targetTouches.length !== 1) {
                    return;
                }

                let [scrollParent] = scrollParents(e.target, /auto|scroll/);
                if (!within(scrollParent, el)) {
                    scrollParent = el;
                }

                const clientY = e.targetTouches[0].clientY - startClientY;
                const { scrollTop, scrollHeight, clientHeight } = scrollParent;

                if (
                    clientHeight >= scrollHeight ||
                    (scrollTop === 0 && clientY > 0) ||
                    (scrollHeight - scrollTop <= clientHeight && clientY < 0)
                ) {
                    e.cancelable && e.preventDefault();
                }
            },
            { passive: false }
        ),
    ];

    return () => events.forEach((fn) => fn());
}

export function preventBackgroundScroll() {
    const { body, documentElement } = document;
    css(body, {
        overflowY: width(window) > documentElement.clientWidth ? 'scroll' : '',
        touchAction: 'none',
    });
    css(documentElement, 'overflowY', 'hidden');
    return () => {
        css(documentElement, 'overflowY', '');
        css(body, { overflowY: '', touchAction: '' });
    };
}

function filterChildren(el, fn) {
    const children = [];
    apply(el, (node) => {
        if (fn(node)) {
            children.push(node);
        }
    });
    return children;
}
<commit_msg>fix: modal overflow handling<commit_after>import {
    $,
    addClass,
    append,
    apply,
    attr,
    css,
    endsWith,
    includes,
    isFocusable,
    last,
    noop,
    on,
    once,
    parent,
    pointerCancel,
    pointerDown,
    pointerUp,
    removeClass,
    scrollParents,
    toFloat,
    width,
    within,
} from 'uikit-util';
import Class from './class';
import Container from './container';
import Togglable from './togglable';

const active = [];

export default {
    mixins: [Class, Container, Togglable],

    props: {
        selPanel: String,
        selClose: String,
        escClose: Boolean,
        bgClose: Boolean,
        stack: Boolean,
    },

    data: {
        cls: 'uk-open',
        escClose: true,
        bgClose: true,
        overlay: true,
        stack: false,
    },

    computed: {
        panel({ selPanel }, $el) {
            return $(selPanel, $el);
        },

        transitionElement() {
            return this.panel;
        },

        bgClose({ bgClose }) {
            return bgClose && this.panel;
        },
    },

    beforeDisconnect() {
        if (includes(active, this)) {
            this.toggleElement(this.$el, false, false);
        }
    },

    events: [
        {
            name: 'click',

            delegate() {
                return this.selClose;
            },

            handler(e) {
                e.preventDefault();
                this.hide();
            },
        },

        {
            name: 'toggle',

            self: true,

            handler(e) {
                if (e.defaultPrevented) {
                    return;
                }

                e.preventDefault();

                if (this.isToggled() === includes(active, this)) {
                    this.toggle();
                }
            },
        },

        {
            name: 'beforeshow',

            self: true,

            handler(e) {
                if (includes(active, this)) {
                    return false;
                }

                if (!this.stack && active.length) {
                    Promise.all(active.map((modal) => modal.hide())).then(this.show);
                    e.preventDefault();
                } else {
                    active.push(this);
                }
            },
        },

        {
            name: 'show',

            self: true,

            handler() {
                once(
                    this.$el,
                    'hide',
                    on(document, 'focusin', (e) => {
                        if (last(active) === this && !within(e.target, this.$el)) {
                            this.$el.focus();
                        }
                    })
                );

                if (this.overlay) {
                    once(this.$el, 'hidden', preventOverscroll(this.$el));
                    once(this.$el, 'hidden', preventBackgroundScroll());
                }

                if (this.stack) {
                    css(this.$el, 'zIndex', toFloat(css(this.$el, 'zIndex')) + active.length);
                }

                addClass(document.documentElement, this.clsPage);

                if (this.bgClose) {
                    once(
                        this.$el,
                        'hide',
                        on(document, pointerDown, ({ target }) => {
                            if (
                                last(active) !== this ||
                                (this.overlay && !within(target, this.$el)) ||
                                within(target, this.panel)
                            ) {
                                return;
                            }

                            once(
                                document,
                                `${pointerUp} ${pointerCancel} scroll`,
                                ({ defaultPrevented, type, target: newTarget }) => {
                                    if (
                                        !defaultPrevented &&
                                        type === pointerUp &&
                                        target === newTarget
                                    ) {
                                        this.hide();
                                    }
                                },
                                true
                            );
                        }),
                        { self: true }
                    );
                }

                if (this.escClose) {
                    once(
                        this.$el,
                        'hide',
                        on(document, 'keydown', (e) => {
                            if (e.keyCode === 27 && last(active) === this) {
                                this.hide();
                            }
                        }),
                        { self: true }
                    );
                }
            },
        },

        {
            name: 'shown',

            self: true,

            handler() {
                if (!isFocusable(this.$el)) {
                    attr(this.$el, 'tabindex', '-1');
                }

                if (!$(':focus', this.$el)) {
                    this.$el.focus();
                }
            },
        },

        {
            name: 'hidden',

            self: true,

            handler() {
                if (includes(active, this)) {
                    active.splice(active.indexOf(this), 1);
                }

                css(this.$el, 'zIndex', '');

                if (!active.some((modal) => modal.clsPage === this.clsPage)) {
                    removeClass(document.documentElement, this.clsPage);
                }
            },
        },
    ],

    methods: {
        toggle() {
            return this.isToggled() ? this.hide() : this.show();
        },

        show() {
            if (this.container && parent(this.$el) !== this.container) {
                append(this.container, this.$el);
                return new Promise((resolve) =>
                    requestAnimationFrame(() => this.show().then(resolve))
                );
            }

            return this.toggleElement(this.$el, true, animate(this));
        },

        hide() {
            return this.toggleElement(this.$el, false, animate(this));
        },
    },
};

function animate({ transitionElement, _toggle }) {
    return (el, show) =>
        new Promise((resolve, reject) =>
            once(el, 'show hide', () => {
                el._reject?.();
                el._reject = reject;

                _toggle(el, show);

                const off = once(
                    transitionElement,
                    'transitionstart',
                    () => {
                        once(transitionElement, 'transitionend transitioncancel', resolve, {
                            self: true,
                        });
                        clearTimeout(timer);
                    },
                    { self: true }
                );

                const timer = setTimeout(() => {
                    off();
                    resolve();
                }, toMs(css(transitionElement, 'transitionDuration')));
            })
        ).then(() => delete el._reject);
}

function toMs(time) {
    return time ? (endsWith(time, 'ms') ? toFloat(time) : toFloat(time) * 1000) : 0;
}

export function preventOverscroll(el) {
    if (CSS.supports('overscroll-behavior', 'contain')) {
        const elements = filterChildren(el, (child) => /auto|scroll/.test(css(child, 'overflow')));
        css(elements, 'overscrollBehavior', 'contain');
        return () => css(elements, 'overscrollBehavior', '');
    }

    let startClientY;

    const events = [
        on(
            el,
            'touchstart',
            ({ targetTouches }) => {
                if (targetTouches.length === 1) {
                    startClientY = targetTouches[0].clientY;
                }
            },
            { passive: true }
        ),

        on(
            el,
            'touchmove',
            (e) => {
                if (e.targetTouches.length !== 1) {
                    return;
                }

                let [scrollParent] = scrollParents(e.target, /auto|scroll/);
                if (!within(scrollParent, el)) {
                    scrollParent = el;
                }

                const clientY = e.targetTouches[0].clientY - startClientY;
                const { scrollTop, scrollHeight, clientHeight } = scrollParent;

                if (
                    clientHeight >= scrollHeight ||
                    (scrollTop === 0 && clientY > 0) ||
                    (scrollHeight - scrollTop <= clientHeight && clientY < 0)
                ) {
                    e.cancelable && e.preventDefault();
                }
            },
            { passive: false }
        ),
    ];

    return () => events.forEach((fn) => fn());
}

let prevented;
export function preventBackgroundScroll() {
    if (prevented) {
        return noop;
    }
    prevented = true;
    const { body, documentElement } = document;
    css(body, {
        overflowY: width(window) > documentElement.clientWidth ? 'scroll' : '',
        touchAction: 'none',
    });
    css(documentElement, 'overflowY', 'hidden');
    return () => {
        prevented = false;
        css(documentElement, 'overflowY', '');
        css(body, { overflowY: '', touchAction: '' });
    };
}

function filterChildren(el, fn) {
    const children = [];
    apply(el, (node) => {
        if (fn(node)) {
            children.push(node);
        }
    });
    return children;
}
<|endoftext|>


<commit_before>;(function(Planner, Events, Templates, Helpers, Utils){ 'use strict';

    Planner.Model = Planner.Model || {};

    // Card class definition with prototype methods
    // --------------------------------------------

    var Card = function(args) {
        this.id = args.id;
        this.title = args.title;
        this.content = args.content;
        this.start = args.start;
        this.end = args.end;
        this.assignees = args.assignees;

        // Generate DOM if all required arguments are defined
        if(typeof this.start !== 'undefined' && typeof this.end !== 'undefined' && typeof this.assignees !== 'undefined') {
            this.generateDOM();
        }
    };

    // Card class definition with prototype methods
    // --------------------------------------------

    Card.prototype.generateTitle = function() {
        var self = this;
        var title;

        if (typeof self.start.getHours === 'function' && typeof self.start.getMinutes === 'function' && typeof self.end.getHours === 'function' && typeof self.end.getMinutes === 'function') {
            title = Utils.pad(self.start.getHours()) + ':' + Utils.pad(self.start.getMinutes()) + ' - ' + Utils.pad(self.end.getHours()) + ':' + Utils.pad(self.end.getMinutes());
        } else if (self.start === self.end) {
            title = self.start;
        } else {
            title = self.start + '-' + self.end;
        }

        return title;
    };

    Card.prototype.generateDOM = function() {
        var options = Planner.options;
        var self = this;
        self.titleHeader = self.generateTitle();

        // Generate a standard Card DOM object
        var generatedElements = [];
        var cardDOM = $(Templates.card(self));
        var element;

        // Generate DOM element for each assignees
        this.assignees.forEach(function(assignee) {
            element = cardDOM.clone();

            // Convert Card attributes to DOM attributes
            var start = Helpers.attributeToIndex(self.start);
            var end = Helpers.attributeToIndex(self.end);
            var cardLength = (end - start) * options.timeslotHeight - options.cardTitleMargin;

            // Set DOM values
            element.data('column', assignee);
            element.data('start', start);
            element.data('end', end);
            element.height(cardLength);

            // Publish click event on Card DOM element
            element.on('mousedown', function(event) {
                Events.publish('cardClicked', [self, element]);

                // Avoid propagation of element on other DOM elements
                event.stopPropagation();
            });

            generatedElements.push(element);
            Events.publish('cardCreated', [self, element]);
        });

        self.$element = generatedElements;
    };

    // Methods that should be overridden by your
    // application to enable persistence
    // -----------------------------------------

    Card.prototype.save = function() {};
    Card.prototype.delete = function() {};

    Planner.Model.Card = Card;

})(Planner, Planner.Events, Planner.Templates, Planner.Helpers, Planner.Utils);
<commit_msg>Removed persistence methods from model. Managed by Pub/Sub pattern<commit_after>;(function(Planner, Events, Templates, Helpers, Utils){ 'use strict';

    Planner.Model = Planner.Model || {};

    // Card class definition with prototype methods
    // --------------------------------------------

    var Card = function(args) {
        this.id = args.id;
        this.title = args.title;
        this.content = args.content;
        this.start = args.start;
        this.end = args.end;
        this.assignees = args.assignees;

        // Generate DOM if all required arguments are defined
        if(typeof this.start !== 'undefined' && typeof this.end !== 'undefined' && typeof this.assignees !== 'undefined') {
            this.generateDOM();
        }
    };

    // Card class definition with prototype methods
    // --------------------------------------------

    Card.prototype.generateTitle = function() {
        var self = this;
        var title;

        if (typeof self.start.getHours === 'function' && typeof self.start.getMinutes === 'function' && typeof self.end.getHours === 'function' && typeof self.end.getMinutes === 'function') {
            title = Utils.pad(self.start.getHours()) + ':' + Utils.pad(self.start.getMinutes()) + ' - ' + Utils.pad(self.end.getHours()) + ':' + Utils.pad(self.end.getMinutes());
        } else if (self.start === self.end) {
            title = self.start;
        } else {
            title = self.start + '-' + self.end;
        }

        return title;
    };

    Card.prototype.generateDOM = function() {
        var options = Planner.options;
        var self = this;
        self.titleHeader = self.generateTitle();

        // Generate a standard Card DOM object
        var generatedElements = [];
        var cardDOM = $(Templates.card(self));
        var element;

        // Generate DOM element for each assignees
        this.assignees.forEach(function(assignee) {
            element = cardDOM.clone();

            // Convert Card attributes to DOM attributes
            var start = Helpers.attributeToIndex(self.start);
            var end = Helpers.attributeToIndex(self.end);
            var cardLength = (end - start) * options.timeslotHeight - options.cardTitleMargin;

            // Set DOM values
            element.data('column', assignee);
            element.data('start', start);
            element.data('end', end);
            element.height(cardLength);

            // Publish click event on Card DOM element
            element.on('mousedown', function(event) {
                Events.publish('cardClicked', [self, element]);

                // Avoid propagation of element on other DOM elements
                event.stopPropagation();
            });

            generatedElements.push(element);
            Events.publish('cardCreated', [self, element]);
        });

        self.$element = generatedElements;
    };

    Planner.Model.Card = Card;

})(Planner, Planner.Events, Planner.Templates, Planner.Helpers, Planner.Utils);
<|endoftext|>


<commit_before>'use strict'

const ko = require('knockout')
const moment = require('moment')

const adminUrls = require('../admin-urls')
const ajax = require('../ajax')
const BaseViewModel = require('./BaseViewModel')
const browser = require('../browser')
const Endpoints = require('../endpoint-builder')
const htmlEncode = require('htmlencode')
const dateFormat = 'YYYY-MM-DD'

import { clientGroups } from '../../data/generated/client-groups'

function Need (data) {
  const self = this
  self.endpoints = new Endpoints()

  self.serviceProviderId = data.serviceProviderId
  self.availableTypes = ko.observableArray(['money', 'time', 'items'])
  self.availableClientGroups = ko.observableArray(clientGroups)

  if (data.clientGroups !== null && data.clientGroups !== undefined && data.clientGroups.length > 0) {
    self.сlientGroupKeys = ko.observableArray(data.clientGroups.map((v) => v.key))
  } else {
    self.сlientGroupKeys = ko.observableArray([])
  }

  self.id = ko.observable(data.id)
  self.description = ko.observable(htmlEncode.htmlDecode(data.description))
  self.type = ko.observable(data.type)
  self.isPeopleOrThings = ko.computed(function () {
    const type = self.type()
    return type !== undefined && (type.toLowerCase() === 'time' || type.toLowerCase() === 'items')
  }, self)
  self.isMoney = ko.computed(function () {
    const type = self.type()
    return type !== undefined && (type.toLowerCase() === 'money')
  }, self)
  self.reason = ko.observable(htmlEncode.htmlDecode(data.reason))
  self.moreInfoUrl = ko.observable(data.moreInfoUrl)
  self.startDate = ko.observable(data.neededDate ? moment(data.neededDate).format(dateFormat) : moment().format(dateFormat))
  self.endDate = ko.observable(data.endDate ? moment(data.endDate).format(dateFormat) : moment().add('1', 'days').format(dateFormat))
  self.postcode = ko.observable(data.postcode)
  self.instructions = ko.observable(htmlEncode.htmlDecode(data.instructions))
  self.email = ko.observable(data.email)
  self.donationAmountInPounds = ko.observable(data.donationAmountInPounds)
  self.donationUrl = ko.observable(data.donationUrl)
  self.keywords = ko.observable(data.keywords !== undefined && data.keywords !== null ? data.keywords.join(', ') : '')
  self.customMessage = ko.observable(data.customMessage)
  self.isPriority = ko.observable(data.isPriority)

  self.neededDateReadOnly = ko.observable(moment(data.neededDate))
  self.neededDate = ko.computed(function () {
    return self.neededDateReadOnly().fromNow()
  }, self)
  self.canRepost = ko.computed(function () {
    const now = moment()
    const diff = now.diff(self.neededDateReadOnly(), 'days')
    return diff >= 7
  }, self)

  self.tempKey = ko.observable(data.tempKey)
  self.isEditing = ko.observable(false)
  self.listeners = ko.observableArray()

  self.editNeedUrl = adminUrls.serviceProviderNeedsEdit + '?providerId=' + self.serviceProviderId + '&needId=' + self.id()

  self.viewOffersUrl = `${adminUrls.needResponses}?needId=${self.id()}`

  self.totalResponses = ko.observable(0)

  self.startDate.subscribe((value) => {
    if(moment(value) > moment(self.endDate()).add('-1', 'days')){
      self.endDate(moment(value).add('1', 'days').format(dateFormat))
    }
  })

  self.repostNeed = function () {
    browser.loading()
    const endpoint = self.endpointBuilder
      .serviceProviders(self.serviceProviderId)
      .needs(self.id())
      .build() + '/neededDate'
    const now = moment()
    const model = {
      NeededDate: now.toISOString()
    }
    ajax.put(endpoint,
      model
    ).then(function (result) {
      if (result.statusCode === 200) {
        self.neededDateReadOnly(now)
        browser.loaded()
      } else {
        self.handleError(result)
      }
    }, function (error) {
      self.handleError(error)
    })
  }

  self.deleteNeed = function () {
    var endpoint = `${self.endpointBuilder.serviceProviders(self.serviceProviderId).needs(self.id()).build()}`
    ajax.delete(endpoint)
    .then(function (result) {
      self.listeners().forEach((l) => l.deleteNeed(self))
    }, function (error) {
      self.handleError(error)
    })
  }

  self.resolveNeed = function () {
    var endpoint = `${self.endpointBuilder.serviceProviders(self.serviceProviderId).needs(self.id()).build()}/is-resolved`
    ajax.patch(endpoint, { IsResolved: true })
    .then(function (result) {
      self.listeners().forEach((l) => l.deleteNeed(self))
    }, function (error) {
      self.handleError(error)
    })
  }

  self.markAsPriority = function () {
    var endpoint = `${self.endpointBuilder.serviceProviders(self.serviceProviderId).needs(self.id()).build()}/is-priority`
    ajax.patch(endpoint, { IsPriority: !self.isPriority() })
    .then(function (result) {
      self.isPriority(!self.isPriority())
    }, function (error) {
      self.handleError(error)
    })
  }

  self.save = function () {
    self.isProcessing(true)
    let keywords = self.keywords() !== undefined
      ? self.keywords().split(',').map((k) => k.trim())
      : []
    var model = {
      'Description': self.description(),
      'Type': self.type(),
      'Reason': self.reason(),
      'MoreInfoUrl': self.moreInfoUrl(),
      'Postcode': self.postcode(),
      'Instructions': self.instructions(),
      'Email': self.email(),
      'DonationAmountInPounds': self.donationAmountInPounds(),
      'DonationUrl': self.donationUrl(),
      'CustomMessage': self.customMessage(),
      'Keywords': keywords,
      'ClientGroupKeys': self.сlientGroupKeys(),
      'NeededDate': self.startDate(),
      'EndDate': self.endDate()
    }

    if (self.id() === undefined) { // adding
      browser.loading()
      ajax.post(self.endpointBuilder.serviceProviders(self.serviceProviderId).needs().build(),
        model
      ).then(function (result) {
        browser.loaded()
        self.isProcessing(false)
        if (result.statusCode === 201) {
          self.listeners().forEach((l) => l.saveNeed(self))
        } else {
          self.handleError(result)
        }
      }, function (error) {
        self.handleError(error)
      })
    } else { // editing
      let endpoint = self.endpointBuilder.serviceProviders(self.serviceProviderId).needs(self.id()).build()
      ajax.put(endpoint,
        model
      ).then(function (result) {
        self.isProcessing(false)
        if (result.statusCode === 200) {
          self.listeners().forEach((l) => l.saveNeed(self))
        } else {
          self.handleError(result)
        }
      }, function (error) {
        self.handleError(error)
      })
    }
  }

  self.addListener = function (listener) {
    self.listeners().push(listener)
  }

  if (self.id()) {
    ajax
      .get(`${self.endpoints.serviceProviderNeeds(self.id()).build()}/offers-to-help`)
      .then((result) => {
        self.totalResponses(result.data.helpOffers.length)
      })
  }
}

Need.prototype = new BaseViewModel()

module.exports = Need
<commit_msg>Update date<commit_after>'use strict'

const ko = require('knockout')
const moment = require('moment')

const adminUrls = require('../admin-urls')
const ajax = require('../ajax')
const BaseViewModel = require('./BaseViewModel')
const browser = require('../browser')
const Endpoints = require('../endpoint-builder')
const htmlEncode = require('htmlencode')
const dateFormat = 'YYYY-MM-DD'

import { clientGroups } from '../../data/generated/client-groups'

function Need (data) {
  const self = this
  self.endpoints = new Endpoints()

  self.serviceProviderId = data.serviceProviderId
  self.availableTypes = ko.observableArray(['money', 'time', 'items'])
  self.availableClientGroups = ko.observableArray(clientGroups)

  if (data.clientGroups !== null && data.clientGroups !== undefined && data.clientGroups.length > 0) {
    self.сlientGroupKeys = ko.observableArray(data.clientGroups.map((v) => v.key))
  } else {
    self.сlientGroupKeys = ko.observableArray([])
  }

  self.id = ko.observable(data.id)
  self.description = ko.observable(htmlEncode.htmlDecode(data.description))
  self.type = ko.observable(data.type)
  self.isPeopleOrThings = ko.computed(function () {
    const type = self.type()
    return type !== undefined && (type.toLowerCase() === 'time' || type.toLowerCase() === 'items')
  }, self)
  self.isMoney = ko.computed(function () {
    const type = self.type()
    return type !== undefined && (type.toLowerCase() === 'money')
  }, self)
  self.reason = ko.observable(htmlEncode.htmlDecode(data.reason))
  self.moreInfoUrl = ko.observable(data.moreInfoUrl)
  self.startDate = ko.observable(data.neededDate ? moment(data.neededDate).format(dateFormat) : moment().format(dateFormat))
  self.endDate = ko.observable(data.endDate ? moment(data.endDate).format(dateFormat) : moment().add('1', 'days').format(dateFormat))
  self.postcode = ko.observable(data.postcode)
  self.instructions = ko.observable(htmlEncode.htmlDecode(data.instructions))
  self.email = ko.observable(data.email)
  self.donationAmountInPounds = ko.observable(data.donationAmountInPounds)
  self.donationUrl = ko.observable(data.donationUrl)
  self.keywords = ko.observable(data.keywords !== undefined && data.keywords !== null ? data.keywords.join(', ') : '')
  self.customMessage = ko.observable(data.customMessage)
  self.isPriority = ko.observable(data.isPriority)

  self.neededDateReadOnly = ko.observable(moment(data.neededDate))
  self.neededDate = ko.computed(function () {
    return self.neededDateReadOnly().fromNow()
  }, self)
  self.canRepost = ko.computed(function () {
    const now = moment()
    const diff = now.diff(self.neededDateReadOnly(), 'days')
    return diff >= 7
  }, self)

  self.tempKey = ko.observable(data.tempKey)
  self.isEditing = ko.observable(false)
  self.listeners = ko.observableArray()

  self.editNeedUrl = adminUrls.serviceProviderNeedsEdit + '?providerId=' + self.serviceProviderId + '&needId=' + self.id()

  self.viewOffersUrl = `${adminUrls.needResponses}?needId=${self.id()}`

  self.totalResponses = ko.observable(0)

  self.startDate.subscribe((value) => {
    if(moment(value) > moment(self.endDate()).add('-1', 'days')){
      self.endDate(moment(value).add('1', 'days').format(dateFormat))
    }
  })

  self.repostNeed = function () {
    browser.loading()
    const endpoint = self.endpointBuilder
      .serviceProviders(self.serviceProviderId)
      .needs(self.id())
      .build() + '/neededDate'
    const now = moment()
    const model = {
      NeededDate: now.toISOString()
    }
    ajax.put(endpoint,
      model
    ).then(function (result) {
      if (result.statusCode === 200) {
        self.neededDateReadOnly(now)
        browser.loaded()
      } else {
        self.handleError(result)
      }
    }, function (error) {
      self.handleError(error)
    })
  }

  self.deleteNeed = function () {
    var endpoint = `${self.endpointBuilder.serviceProviders(self.serviceProviderId).needs(self.id()).build()}`
    ajax.delete(endpoint)
    .then(function (result) {
      self.listeners().forEach((l) => l.deleteNeed(self))
    }, function (error) {
      self.handleError(error)
    })
  }

  self.resolveNeed = function () {
    var endpoint = `${self.endpointBuilder.serviceProviders(self.serviceProviderId).needs(self.id()).build()}/is-resolved`
    ajax.patch(endpoint, { IsResolved: true })
    .then(function (result) {
      self.listeners().forEach((l) => l.deleteNeed(self))
    }, function (error) {
      self.handleError(error)
    })
  }

  self.markAsPriority = function () {
    var endpoint = `${self.endpointBuilder.serviceProviders(self.serviceProviderId).needs(self.id()).build()}/is-priority`
    ajax.patch(endpoint, { IsPriority: !self.isPriority() })
    .then(function (result) {
      self.isPriority(!self.isPriority())
    }, function (error) {
      self.handleError(error)
    })
  }

  self.save = function () {
    self.isProcessing(true)
    let keywords = self.keywords() !== undefined
      ? self.keywords().split(',').map((k) => k.trim())
      : []
    var model = {
      'Description': self.description(),
      'Type': self.type(),
      'Reason': self.reason(),
      'MoreInfoUrl': self.moreInfoUrl(),
      'Postcode': self.postcode(),
      'Instructions': self.instructions(),
      'Email': self.email(),
      'DonationAmountInPounds': self.donationAmountInPounds(),
      'DonationUrl': self.donationUrl(),
      'CustomMessage': self.customMessage(),
      'Keywords': keywords,
      'ClientGroupKeys': self.сlientGroupKeys(),
      'NeededDate': new Date(self.startDate()),
      'EndDate': new Date(self.endDate())
    }

    if (self.id() === undefined) { // adding
      browser.loading()
      ajax.post(self.endpointBuilder.serviceProviders(self.serviceProviderId).needs().build(),
        model
      ).then(function (result) {
        browser.loaded()
        self.isProcessing(false)
        if (result.statusCode === 201) {
          self.listeners().forEach((l) => l.saveNeed(self))
        } else {
          self.handleError(result)
        }
      }, function (error) {
        self.handleError(error)
      })
    } else { // editing
      let endpoint = self.endpointBuilder.serviceProviders(self.serviceProviderId).needs(self.id()).build()
      ajax.put(endpoint,
        model
      ).then(function (result) {
        self.isProcessing(false)
        if (result.statusCode === 200) {
          self.listeners().forEach((l) => l.saveNeed(self))
        } else {
          self.handleError(result)
        }
      }, function (error) {
        self.handleError(error)
      })
    }
  }

  self.addListener = function (listener) {
    self.listeners().push(listener)
  }

  if (self.id()) {
    ajax
      .get(`${self.endpoints.serviceProviderNeeds(self.id()).build()}/offers-to-help`)
      .then((result) => {
        self.totalResponses(result.data.helpOffers.length)
      })
  }
}

Need.prototype = new BaseViewModel()

module.exports = Need
<|endoftext|>


<commit_before>(function(L) {
  'use strict';

  L.views.NoteView = Backbone.View.extend({
    tagName: 'li',
    className: 'note hbox justified_box',
    initialize: function(options) {
      var that = this;
      $(window).one('beforeunload', function() {
        that.undelegateEvents();
        that.stopListening();
        that.closeEditor();
      });
      this.template = L.templates["note"];
      this.listenTo(this.model, 'change:contents', _.mask(this.updateContents, 2));
      this.listenTo(this.model, 'change:meta', _.mask(this.updateMeta, 2));
    },
    remove : function(options) {
      if (this._rendered) {
        this.closeEditor();
        this.cleanupEditor();
        var el = this.$el;
        if (options && options.animate) {
          el.stop().fadeOut({queue: false}, 200).slideUp(300, function() {
            el.remove();
          });
        } else {
          el.remove();
        }
      }
    },
    isVisible: function() {
      // This assumes that el having a parent means that it is visible. Much
      // faster than checking $el.is(':visible') and speed is important here.
      return this.el && this.el.parentNode;
    },
    render: function(options) {
      this.$el.prop("id", "note-"+this.model.id);
      this.$el.attr("data-note", this.model.id);

      if (this._rendered) {
        this.closeEditor();
        this.undelegateEvents();
        this.delegateEvents();
      } else {
        this._rendered = true;
        this.$el.html(this.template(this.model.toJSON()));
        this.updateMeta();
      }

      return this;
    },
    cleanupEditor: function() {
      if (this.editor) {
        this.editor.remove();
        delete this.editor;
      }
    },
    updateMeta: function(options) {
      this.$el.prop('className', this.className);
      if (this.model.get('meta', {}).pinned) {
        this.$el.addClass('pinned');
      } else {
        this.$el.removeClass('pinned');
      }
    },
    updateContents: function(options) {
      this.$el.children('.contents').html(this.model.get('contents'));
    },
    events: {
      'click     .close-btn':             'onRemoveClicked',
      'click     .contents':              'onClick',
      'click     .contents a':            'onLinkOpen',
      'click     .contents .listit_tag':  'onTagClick',
      'keyup     .contents':              'onKeyUp',
      'blur      .editor':                'onBlur',
      'keydown   .editor':                'onKeyDown',
      'click     .pin-icon':              'onPinToggle',
      'resize    .editor':                'onResizeEditor',
      'mousedown .pin-icon':              function(event){event.preventDefault();}
    },
    onLinkOpen: function(event) {
      this.model.trigger('user:open-bookmark', this.model, this, event.target.href);
      this.stopPropagation();
    },
    onRemoveClicked: function() {
      L.notebook.trashNote(this.model);
      return false;
    },
    onResizeEditor: function() {
      this.$el.scrollIntoView();
    },
    onTagClick: function(event) {
      L.omnibox.tagToggle(event.target.textContent);
      return false;
    },
    expand: function() {
      this.$el.css('height', 'auto');
    },
    collapse: function() {
      this.$el.css('height', '');
    },
    openEditor: function() {
      // I know that this is unreadable. But it makes a point.
      // (it's complicated)

      var $contentsEl = this.$('.contents'),
          $editorEl = this.$('.editor-container');
      if ($editorEl.is(":visible")) {
        return; // Already open
      }
      if (!this.editor) {
        this.editor = new ListIt.views.Editor({
          text: this.model.get('contents')
        });
        $editorEl.html(this.editor.render().el);
      } else {
        this.editor.setText(this.model.get('contents'));
      }
      $editorEl.show();
      $contentsEl.hide();
      this.$el.trigger('startediting');
      this.editor.focus();
      this.model.trigger('user:edit', this.model, this);
    },
    onBlur: function() {
      var that = this;
      if (!this.editor || this.editor.isShowingDialog()) {
        return;
      }
      _.defer(function() {
        var focusedEditor = $(document.activeElement).closest('.editor').get(0),
            myEditor = that.$('.editor').get(0);

        if (focusedEditor !== myEditor) {
          that.closeEditor();
        }
      });
    },
    closeEditor: function() {
      var $contentsEl = this.$('.contents'),
          $editorEl = this.$('.editor-container');
      if ($editorEl.is(":visible")) {
        this.storeText();
        this.collapse();
        $editorEl.hide();
        this.$el.trigger('stopediting');
      }
      // Always show contents.
      $contentsEl.show();
    },
    onClick: function(e) {
      this.expand();
      this.openEditor();
    },
    /**
     * Scroll List so note selected with tab is at top.
     **/
    onKeyUp: function(event) {
      if (event.keyCode === KeyCode.TAB) {
        this.$el.scrollIntoView();
        $('#notes')[0].scrollTop -= 4;
      }
    },
    onKeyDown: function(event) {
      if ((
        event.keyCode === KeyCode.ESC ||
        (event.keyCode === KeyCode.ENTER && event.shiftKey) ||
        (event.which === KeyCode.S && event.ctrlKey)
        ) && this.editor && !this.editor.isShowingDialog()) {
        event.preventDefault();
        this.closeEditor();
      }
    },
    storeText: function() {
      this.model.changeContents(this.editor.getText(), window);
      this.model.trigger('user:save', this.model, this);
    },
    //if editor is visible, gets/sets text through editor - otherwise, through model
    onPinToggle: function(event) {
      if (this.$('.editor-container').is(":visible")) {
        if (L.util.strip(this.editor.getText())[0] === '!') {
          this.editor.replaceText(/!+ */, '');
          this.$el.removeClass('pinned');
        } else {
          this.editor.prependText('! ');
          this.$el.addClass('pinned');
        }
      } else {
        var text = this.model.get('contents');
        if (this.model.get('meta').pinned) {
          text = text.replace(/!+ */, '');
        } else {
          text = '! ' + text;
        }
        this.model.changeContents(text);
      }
    }
    /*onCancel: function(event){
      var $contentsEl = this.$('.contents'),
          $editorEl = this.$('.editor-container');
      if ($editorEl.is(":visible")) {
        this.collapse();
        $editorEl.hide();
        this.$el.trigger('stopediting');
      }
      // Always show contents.
      $contentsEl.show();
    },*/
  });

  L.views.NoteCollectionView = Backbone.View.extend({
    defaults: {
      noteHeight: '1.3em', //L.base.oneLineHeight, //'1.2em',
      lastFocusedNote: null
    },
    initialize: function() {
      var that = this;
      this.subViews = {}; // Note views
      this.renderNext = 0;
      this.listenTo(this.collection, 'add', _.mask(this.addNote, 0));
      this.listenTo(this.collection, 'remove', function(note, col, options) {
        that.removeNote(note, _.defaults({}, options));
      });
      this.listenTo(this.collection, 'reset', _.mask(this.reset, 1));
      this.listenTo(this.collection, 'sort', _.mask(this.sort));
      this.listenTo(L.preferences, 'change:shrinkNotes', this.updateNoteShrinkState);
      $(window).one('beforeunload', function() {
        that.undelegateEvents();
        that.stopListening();
      });
      // Load more if needed on window resize.
      $(window).on("resize", function() {
        if (that._rendered) {
          that.lazyLoadMore();
        }
      });
    },
    events: {
      'scroll': 'onScroll',
      'stopediting .note': 'onStopEditing',
      'startediting .note': 'onStartEditing'
    },
    onStartEditing: function() {
      this.$('#notes-container').sortable("disable");
    },
    onStopEditing: function() {
      this.$('#notes-container').sortable("enable");
    },
    updateNoteShrinkState : function(model, state) {
      if (!this._rendered) {
        return;
      }

      if (state) {
        this.$el.addClass('shrink');
      } else {
        this.$el.removeClass('shrink');
      }
    },
    checkLoadMore: function() {
      var scrollTop = this.$el.scrollTop(),
          containerHeight = this.$el.parent().height(),
          listHeight = this.$el.children('#notes-container').height();

      return (scrollTop + 2*containerHeight) > listHeight;
    },
    onScroll: function() {
      // Load on scroll.
      this.loadMore();
    },
    loadMore: function() {
      var view, note;
      while (this.collection.size() > this.renderNext && this.shouldRenderAt(this.renderNext)) {
        note = this.collection.at(this.renderNext);
        view = this.subViews[note.id];
        if (!view) {
          view = this.subViews[note.id] = new L.views.NoteView({model: note});
        }
        if (!view.isVisible()) {
          this.insertAt(this.renderNext, view);
        }
        this.renderNext += 1;
      }
    },
    lazyLoadMore: _.debounce(function() {
      return this.loadMore();
    }, 100),
    shouldRenderAt: function(index) {
      var scrollTop = this.$el.scrollTop(),
          containerHeight = this.$el.parent().height(),
          notes = this.$('#notes-container .note'),
          noteOffset;
      if (notes.length > index) {
        noteOffset = notes.eq(index).position().top;
      } else if (notes.length > 0) {
        noteOffset = notes.last().position().top;
      } else {
        noteOffset = 0;
      }
      return (scrollTop + 2*containerHeight) > noteOffset;
    },
    addNote: function(note, idx) {
      var view = this.subViews[note.id];

      // Ignore already visible/unrendered
      if (!this._rendered || view && view.isVisible()) {
        return true;
      }

      // Get the index but allow it to be specified manually.
      var index = (typeof(idx) === "number") ? idx : this.collection.indexOf(note);

      if (this.shouldRenderAt(index)) {
        if (!view) {
          view = this.subViews[note.id] = new L.views.NoteView({model: note});
        }
        this.insertAt(index, view);
        if (index < this.renderNext) {
          this.renderNext += 1;
        }
        this.fixHeight();
        return true;
      } else if (index < this.renderNext) {
        // If I choose not to render, fix the render next index.
        this.renderNext = index;
        return false;
      }
    },
    insertAt: function(index, view) {
      var otherEl = this.$el.children('#notes-container').find('.note').eq(index);
      if (otherEl.length === 0) {
        this.$el.children('#notes-container').append(view.render().$el);
      } else {
        otherEl.before(view.render().$el);
      }
    },
    // Delay removes just in case we are re-adding the note.
    removeNote: function(note, options) {
      var id = note.id || note,
          view = this.subViews[id];

      this.fixHeight();
      if (!view || !this._rendered) {
        return;
      }

      if (0 < this.$('#notes-container .note').index(view.$el) < this.renderNext) {
        this.renderNext--;
      }

      view.remove(options);
      this.lazyLoadMore();
    },
    fixHeight: _.throttle(function() {
      this.$('#notes-container').css("min-height", 36*this.collection.size());
    }, 100),
    sort: _.debounce(function() {
      var that = this;
      this.collection.each(function(note, i) {
        var view = that.subViews[note.id];
        if (view) {
          view.remove();
          that.insertAt(i, view);
        }
      });
    }, 100),
    reset: function() {
      this.renderNext = 0;
      if (this._rendered) {
        this.$el.children('#notes-container').empty();
        // Bail early.
        this.collection.every(_.mask(_.bind(this.addNote, this), 0, 1));
      }
    },
    render: function() {
      if (!this._rendered) {
        var ul = $('<ul>');
        ul.prop({ className: 'notelist', id: 'notes-container' });
        this.$el.html(ul);

        var collection = this.collection.backingCollection;

        // Prevent clicks from getting triggered when sorting.
        var sorting = false;
        ul.get(0).addEventListener("click", function(e) {
          if (sorting) {
            e.stopImmediatePropagation();
          }
        }, true);

        var current;
        ul.sortable({
          distance: 10,
          items: '.note:not(.pinned)',
          containment: 'parent',
          tolerance: 'intersect',
          revert: 100,
          start: function(event, ui) {
            sorting = true;
            ui.placeholder.height(ui.item.height() / 2);
            current = ui.placeholder.next();
          },
          change: function(event, ui) {
            current.css('margin-top', '2px');
            current = ui.placeholder.next();
            current.css('margin-top', ui.placeholder.height());
          },
          stop: function(event, ui) {
            current.css({'-webkit-transition': 'none',
                         '-moz-transition': 'none',
                         'transition': 'none',
                        'margin-top': '2px'});
            var noteId = ui.item.attr('data-note');
            var previousId = ui.item.prev().attr('data-note');
            var note = collection.get(noteId);
            collection.remove(note);
            var insertIndex;
            if (previousId) {
              insertIndex = collection.indexOf(collection.get(previousId))+1;
            } else {
              insertIndex = 0;
            }
            collection.add(note, {at: insertIndex});

            // This needs to be set after the click event has been triggered.
            _.defer(function() { sorting = false; });
          }
        });

        this._rendered = true;
        this.updateNoteShrinkState(L.preferences, L.preferences.get('shrinkNotes'));
      }
      _.defer(_.bind(this.reset, this));
      return this;
    }
  });

})(ListIt);
<commit_msg>Call stopPropagation on the correct object.<commit_after>(function(L) {
  'use strict';

  L.views.NoteView = Backbone.View.extend({
    tagName: 'li',
    className: 'note hbox justified_box',
    initialize: function(options) {
      var that = this;
      $(window).one('beforeunload', function() {
        that.undelegateEvents();
        that.stopListening();
        that.closeEditor();
      });
      this.template = L.templates["note"];
      this.listenTo(this.model, 'change:contents', _.mask(this.updateContents, 2));
      this.listenTo(this.model, 'change:meta', _.mask(this.updateMeta, 2));
    },
    remove : function(options) {
      if (this._rendered) {
        this.closeEditor();
        this.cleanupEditor();
        var el = this.$el;
        if (options && options.animate) {
          el.stop().fadeOut({queue: false}, 200).slideUp(300, function() {
            el.remove();
          });
        } else {
          el.remove();
        }
      }
    },
    isVisible: function() {
      // This assumes that el having a parent means that it is visible. Much
      // faster than checking $el.is(':visible') and speed is important here.
      return this.el && this.el.parentNode;
    },
    render: function(options) {
      this.$el.prop("id", "note-"+this.model.id);
      this.$el.attr("data-note", this.model.id);

      if (this._rendered) {
        this.closeEditor();
        this.undelegateEvents();
        this.delegateEvents();
      } else {
        this._rendered = true;
        this.$el.html(this.template(this.model.toJSON()));
        this.updateMeta();
      }

      return this;
    },
    cleanupEditor: function() {
      if (this.editor) {
        this.editor.remove();
        delete this.editor;
      }
    },
    updateMeta: function(options) {
      this.$el.prop('className', this.className);
      if (this.model.get('meta', {}).pinned) {
        this.$el.addClass('pinned');
      } else {
        this.$el.removeClass('pinned');
      }
    },
    updateContents: function(options) {
      this.$el.children('.contents').html(this.model.get('contents'));
    },
    events: {
      'click     .close-btn':             'onRemoveClicked',
      'click     .contents':              'onClick',
      'click     .contents a':            'onLinkOpen',
      'click     .contents .listit_tag':  'onTagClick',
      'keyup     .contents':              'onKeyUp',
      'blur      .editor':                'onBlur',
      'keydown   .editor':                'onKeyDown',
      'click     .pin-icon':              'onPinToggle',
      'resize    .editor':                'onResizeEditor',
      'mousedown .pin-icon':              function(event){event.preventDefault();}
    },
    onLinkOpen: function(event) {
      this.model.trigger('user:open-bookmark', this.model, this, event.target.href);
      event.stopPropagation();
    },
    onRemoveClicked: function() {
      L.notebook.trashNote(this.model);
      return false;
    },
    onResizeEditor: function() {
      this.$el.scrollIntoView();
    },
    onTagClick: function(event) {
      L.omnibox.tagToggle(event.target.textContent);
      return false;
    },
    expand: function() {
      this.$el.css('height', 'auto');
    },
    collapse: function() {
      this.$el.css('height', '');
    },
    openEditor: function() {
      // I know that this is unreadable. But it makes a point.
      // (it's complicated)

      var $contentsEl = this.$('.contents'),
          $editorEl = this.$('.editor-container');
      if ($editorEl.is(":visible")) {
        return; // Already open
      }
      if (!this.editor) {
        this.editor = new ListIt.views.Editor({
          text: this.model.get('contents')
        });
        $editorEl.html(this.editor.render().el);
      } else {
        this.editor.setText(this.model.get('contents'));
      }
      $editorEl.show();
      $contentsEl.hide();
      this.$el.trigger('startediting');
      this.editor.focus();
      this.model.trigger('user:edit', this.model, this);
    },
    onBlur: function() {
      var that = this;
      if (!this.editor || this.editor.isShowingDialog()) {
        return;
      }
      _.defer(function() {
        var focusedEditor = $(document.activeElement).closest('.editor').get(0),
            myEditor = that.$('.editor').get(0);

        if (focusedEditor !== myEditor) {
          that.closeEditor();
        }
      });
    },
    closeEditor: function() {
      var $contentsEl = this.$('.contents'),
          $editorEl = this.$('.editor-container');
      if ($editorEl.is(":visible")) {
        this.storeText();
        this.collapse();
        $editorEl.hide();
        this.$el.trigger('stopediting');
      }
      // Always show contents.
      $contentsEl.show();
    },
    onClick: function(e) {
      this.expand();
      this.openEditor();
    },
    /**
     * Scroll List so note selected with tab is at top.
     **/
    onKeyUp: function(event) {
      if (event.keyCode === KeyCode.TAB) {
        this.$el.scrollIntoView();
        $('#notes')[0].scrollTop -= 4;
      }
    },
    onKeyDown: function(event) {
      if ((
        event.keyCode === KeyCode.ESC ||
        (event.keyCode === KeyCode.ENTER && event.shiftKey) ||
        (event.which === KeyCode.S && event.ctrlKey)
        ) && this.editor && !this.editor.isShowingDialog()) {
        event.preventDefault();
        this.closeEditor();
      }
    },
    storeText: function() {
      this.model.changeContents(this.editor.getText(), window);
      this.model.trigger('user:save', this.model, this);
    },
    //if editor is visible, gets/sets text through editor - otherwise, through model
    onPinToggle: function(event) {
      if (this.$('.editor-container').is(":visible")) {
        if (L.util.strip(this.editor.getText())[0] === '!') {
          this.editor.replaceText(/!+ */, '');
          this.$el.removeClass('pinned');
        } else {
          this.editor.prependText('! ');
          this.$el.addClass('pinned');
        }
      } else {
        var text = this.model.get('contents');
        if (this.model.get('meta').pinned) {
          text = text.replace(/!+ */, '');
        } else {
          text = '! ' + text;
        }
        this.model.changeContents(text);
      }
    }
    /*onCancel: function(event){
      var $contentsEl = this.$('.contents'),
          $editorEl = this.$('.editor-container');
      if ($editorEl.is(":visible")) {
        this.collapse();
        $editorEl.hide();
        this.$el.trigger('stopediting');
      }
      // Always show contents.
      $contentsEl.show();
    },*/
  });

  L.views.NoteCollectionView = Backbone.View.extend({
    defaults: {
      noteHeight: '1.3em', //L.base.oneLineHeight, //'1.2em',
      lastFocusedNote: null
    },
    initialize: function() {
      var that = this;
      this.subViews = {}; // Note views
      this.renderNext = 0;
      this.listenTo(this.collection, 'add', _.mask(this.addNote, 0));
      this.listenTo(this.collection, 'remove', function(note, col, options) {
        that.removeNote(note, _.defaults({}, options));
      });
      this.listenTo(this.collection, 'reset', _.mask(this.reset, 1));
      this.listenTo(this.collection, 'sort', _.mask(this.sort));
      this.listenTo(L.preferences, 'change:shrinkNotes', this.updateNoteShrinkState);
      $(window).one('beforeunload', function() {
        that.undelegateEvents();
        that.stopListening();
      });
      // Load more if needed on window resize.
      $(window).on("resize", function() {
        if (that._rendered) {
          that.lazyLoadMore();
        }
      });
    },
    events: {
      'scroll': 'onScroll',
      'stopediting .note': 'onStopEditing',
      'startediting .note': 'onStartEditing'
    },
    onStartEditing: function() {
      this.$('#notes-container').sortable("disable");
    },
    onStopEditing: function() {
      this.$('#notes-container').sortable("enable");
    },
    updateNoteShrinkState : function(model, state) {
      if (!this._rendered) {
        return;
      }

      if (state) {
        this.$el.addClass('shrink');
      } else {
        this.$el.removeClass('shrink');
      }
    },
    checkLoadMore: function() {
      var scrollTop = this.$el.scrollTop(),
          containerHeight = this.$el.parent().height(),
          listHeight = this.$el.children('#notes-container').height();

      return (scrollTop + 2*containerHeight) > listHeight;
    },
    onScroll: function() {
      // Load on scroll.
      this.loadMore();
    },
    loadMore: function() {
      var view, note;
      while (this.collection.size() > this.renderNext && this.shouldRenderAt(this.renderNext)) {
        note = this.collection.at(this.renderNext);
        view = this.subViews[note.id];
        if (!view) {
          view = this.subViews[note.id] = new L.views.NoteView({model: note});
        }
        if (!view.isVisible()) {
          this.insertAt(this.renderNext, view);
        }
        this.renderNext += 1;
      }
    },
    lazyLoadMore: _.debounce(function() {
      return this.loadMore();
    }, 100),
    shouldRenderAt: function(index) {
      var scrollTop = this.$el.scrollTop(),
          containerHeight = this.$el.parent().height(),
          notes = this.$('#notes-container .note'),
          noteOffset;
      if (notes.length > index) {
        noteOffset = notes.eq(index).position().top;
      } else if (notes.length > 0) {
        noteOffset = notes.last().position().top;
      } else {
        noteOffset = 0;
      }
      return (scrollTop + 2*containerHeight) > noteOffset;
    },
    addNote: function(note, idx) {
      var view = this.subViews[note.id];

      // Ignore already visible/unrendered
      if (!this._rendered || view && view.isVisible()) {
        return true;
      }

      // Get the index but allow it to be specified manually.
      var index = (typeof(idx) === "number") ? idx : this.collection.indexOf(note);

      if (this.shouldRenderAt(index)) {
        if (!view) {
          view = this.subViews[note.id] = new L.views.NoteView({model: note});
        }
        this.insertAt(index, view);
        if (index < this.renderNext) {
          this.renderNext += 1;
        }
        this.fixHeight();
        return true;
      } else if (index < this.renderNext) {
        // If I choose not to render, fix the render next index.
        this.renderNext = index;
        return false;
      }
    },
    insertAt: function(index, view) {
      var otherEl = this.$el.children('#notes-container').find('.note').eq(index);
      if (otherEl.length === 0) {
        this.$el.children('#notes-container').append(view.render().$el);
      } else {
        otherEl.before(view.render().$el);
      }
    },
    // Delay removes just in case we are re-adding the note.
    removeNote: function(note, options) {
      var id = note.id || note,
          view = this.subViews[id];

      this.fixHeight();
      if (!view || !this._rendered) {
        return;
      }

      if (0 < this.$('#notes-container .note').index(view.$el) < this.renderNext) {
        this.renderNext--;
      }

      view.remove(options);
      this.lazyLoadMore();
    },
    fixHeight: _.throttle(function() {
      this.$('#notes-container').css("min-height", 36*this.collection.size());
    }, 100),
    sort: _.debounce(function() {
      var that = this;
      this.collection.each(function(note, i) {
        var view = that.subViews[note.id];
        if (view) {
          view.remove();
          that.insertAt(i, view);
        }
      });
    }, 100),
    reset: function() {
      this.renderNext = 0;
      if (this._rendered) {
        this.$el.children('#notes-container').empty();
        // Bail early.
        this.collection.every(_.mask(_.bind(this.addNote, this), 0, 1));
      }
    },
    render: function() {
      if (!this._rendered) {
        var ul = $('<ul>');
        ul.prop({ className: 'notelist', id: 'notes-container' });
        this.$el.html(ul);

        var collection = this.collection.backingCollection;

        // Prevent clicks from getting triggered when sorting.
        var sorting = false;
        ul.get(0).addEventListener("click", function(e) {
          if (sorting) {
            e.stopImmediatePropagation();
          }
        }, true);

        var current;
        ul.sortable({
          distance: 10,
          items: '.note:not(.pinned)',
          containment: 'parent',
          tolerance: 'intersect',
          revert: 100,
          start: function(event, ui) {
            sorting = true;
            ui.placeholder.height(ui.item.height() / 2);
            current = ui.placeholder.next();
          },
          change: function(event, ui) {
            current.css('margin-top', '2px');
            current = ui.placeholder.next();
            current.css('margin-top', ui.placeholder.height());
          },
          stop: function(event, ui) {
            current.css({'-webkit-transition': 'none',
                         '-moz-transition': 'none',
                         'transition': 'none',
                        'margin-top': '2px'});
            var noteId = ui.item.attr('data-note');
            var previousId = ui.item.prev().attr('data-note');
            var note = collection.get(noteId);
            collection.remove(note);
            var insertIndex;
            if (previousId) {
              insertIndex = collection.indexOf(collection.get(previousId))+1;
            } else {
              insertIndex = 0;
            }
            collection.add(note, {at: insertIndex});

            // This needs to be set after the click event has been triggered.
            _.defer(function() { sorting = false; });
          }
        });

        this._rendered = true;
        this.updateNoteShrinkState(L.preferences, L.preferences.get('shrinkNotes'));
      }
      _.defer(_.bind(this.reset, this));
      return this;
    }
  });

})(ListIt);
<|endoftext|>


<commit_before>angular.module('components', [])

.directive('accordions', function() {
  return {
      restrict: 'E', //compiler will match element only
      transclude: true,
      scope: {},//new empty scope, no inheritance -> makes this directive independent as per as scope is concerned

      /* Compiler will call this controller once; because there is a single accordions element. Therefore a good place for initialization */
      controller: function($scope, $element) {
         this.accordions = []; // array for accordion scope.

        this.addAccordion = function(accordion) { // For each instance of accordion instance, this function is called with respective accordion scope
          if (this.accordions.length == 0) this.select(accordion);
          this.accordions.push(accordion);
        }

        this.select = function(accordion) { //accordion is an alias for accordion scope.
          angular.forEach(this.accordions, function(accordion) {
            accordion.selected = false;
          });
          accordion.selected = true;
        }
      },
      template:'

',
      replace: true
    };
  })

.directive('accordion', function() {
  return {
    require: '^accordions', //accordions controller object will be passed as 4th argument in link function.
    restrict: 'E',
    transclude: true,
      /*scope is initialized with title. title will receive value from <accordion title="xxxx">. Instead of this we could also do the following:
      scope: {}, and inside the link function, scope.title=attrs.title
      */
      scope: { title: '@'},
      link: function(scope, element, attrs, accordionsCtrl) {
        accordionsCtrl.addAccordion(scope);// adding accordion scope for each instances of accordion.
        scope.panelCollapse='panel-collapse';//adding css

        scope.select = function(){ //ng-click="select()" parsed in respect to a context and in this case it is scope object. So 'this' denotes scope object
          accordionsCtrl.select(this);
        }
      },
      template:
      '
'+
      '
'+
      '
'+
      ''+
      '{{title}}' +
      '
'+
      '
' +
      '
'+
      '
'+
      '
'+
      '
'+
      '
',
      replace: true
    };
  })<commit_msg>modified comment<commit_after>angular.module('components', [])

.directive('accordions', function() {
  return {
      restrict: 'E', //compiler will match element only
      transclude: true,
      scope: {},//new empty scope, no inheritance -> makes this directive independent as per as scope is concerned

      /* Compiler will call this controller once; because there is a single accordions element. Therefore a good place for initialization */
      controller: function($scope, $element) {
         this.accordions = []; // array for accordion scope.

        this.addAccordion = function(accordion) { // For each instance of accordion instance, this function is called with respective accordion scope
          if (this.accordions.length == 0) this.select(accordion);
          this.accordions.push(accordion);
        }

        this.select = function(accordion) { //accordion is an alias for accordion scope.
          angular.forEach(this.accordions, function(accordion) {
            accordion.selected = false;
          });
          accordion.selected = true;
        }
      },
      template:'

',
      replace: true
    };
  })

.directive('accordion', function() {
  return {
    require: '^accordions', //accordions controller object will be passed as 4th argument in link function.
    restrict: 'E',
    transclude: true,
      /*scope is initialized with title. title will receive value from <accordion title="xxxx">. Instead of this we could also do the following:
      scope: {}, and inside the link function, scope.title=attrs.title
      */
      scope: { title: '@'},
      link: function(scope, element, attrs, accordionsCtrl) {
        accordionsCtrl.addAccordion(scope);// adding accordion scope for each instances of accordion.
        scope.panelCollapse='panel-collapse';//adding css

        scope.select = function(){ //ng-click="select()". select is an expression which is parsed in respect to a context and in this case it is scope object. So 'this' denotes scope object
          accordionsCtrl.select(this);
        }
      },
      template:
      '
'+
      '
'+
      '
'+
      ''+
      '{{title}}' +
      '
'+
      '
' +
      '
'+
      '
'+
      '
'+
      '
'+
      '
',
      replace: true
    };
  })<|endoftext|>


<commit_before>/*
Language: Bash
Author: vah <vahtenberg@gmail.com>
Contributrors: Benjamin Pannell <contact@sierrasoftworks.com>
Category: common
*/

function(hljs) {
  var VAR = {
    className: 'variable',
    variants: [
      {begin: /\$[\w\d#@][\w\d_]*/},
      {begin: /\$\{(.*?)}/}
    ]
  };
  var QUOTE_STRING = {
    className: 'string',
    begin: /"/, end: /"/,
    contains: [
      hljs.BACKSLASH_ESCAPE,
      VAR,
      {
        className: 'variable',
        begin: /\$\(/, end: /\)/,
        contains: [hljs.BACKSLASH_ESCAPE]
      }
    ]
  };
  var APOS_STRING = {
    className: 'string',
    begin: /'/, end: /'/
  };

  return {
    aliases: ['sh', 'zsh'],
    lexemes: /-?[a-z\.]+/,
    keywords: {
      keyword:
        'if then else elif fi for while in do done case esac function',
      literal:
        'true false',
      built_in:
        // Shell built-ins
        // http://www.gnu.org/software/bash/manual/html_node/Shell-Builtin-Commands.html
        'break cd continue eval exec exit export getopts hash pwd readonly return shift test times ' +
        'trap umask unset ' +
        // Bash built-ins
        'alias bind builtin caller command declare echo enable help let local logout mapfile printf ' +
        'read readarray source type typeset ulimit unalias ' +
        // Shell modifiers
        'set shopt ' +
        // Zsh built-ins
        'autoload bg bindkey bye cap chdir clone comparguments compcall compctl compdescribe compfiles ' +
        'compgroups compquote comptags comptry compvalues dirs disable disown echotc echoti emulate ' +
        'fc fg float functions getcap getln history integer jobs kill limit log noglob popd print ' +
        'pushd pushln rehash sched setcap setopt stat suspend ttyctl unfunction unhash unlimit ' +
        'unsetopt vared wait whence where which zcompile zformat zftp zle zmodload zparseopts zprof ' +
        'zpty zregexparse zsocket zstyle ztcp',
      _:
        '-ne -eq -lt -gt -f -d -e -s -l -a' // relevance booster
    },
    contains: [
      {
        className: 'meta',
        begin: /^#![^\n]+sh\s*$/,
        relevance: 10
      },
      {
        className: 'function',
        begin: /\w[\w\d_]*\s*\(\s*\)\s*\{/,
        returnBegin: true,
        contains: [hljs.inherit(hljs.TITLE_MODE, {begin: /\w[\w\d_]*/})],
        relevance: 0
      },
      hljs.HASH_COMMENT_MODE,
      QUOTE_STRING,
      APOS_STRING,
      VAR
    ]
  };
}
<commit_msg>Eat up `_` as part of lexemes in Bash<commit_after>/*
Language: Bash
Author: vah <vahtenberg@gmail.com>
Contributrors: Benjamin Pannell <contact@sierrasoftworks.com>
Category: common
*/

function(hljs) {
  var VAR = {
    className: 'variable',
    variants: [
      {begin: /\$[\w\d#@][\w\d_]*/},
      {begin: /\$\{(.*?)}/}
    ]
  };
  var QUOTE_STRING = {
    className: 'string',
    begin: /"/, end: /"/,
    contains: [
      hljs.BACKSLASH_ESCAPE,
      VAR,
      {
        className: 'variable',
        begin: /\$\(/, end: /\)/,
        contains: [hljs.BACKSLASH_ESCAPE]
      }
    ]
  };
  var APOS_STRING = {
    className: 'string',
    begin: /'/, end: /'/
  };

  return {
    aliases: ['sh', 'zsh'],
    lexemes: /-?[a-z\._]+/,
    keywords: {
      keyword:
        'if then else elif fi for while in do done case esac function',
      literal:
        'true false',
      built_in:
        // Shell built-ins
        // http://www.gnu.org/software/bash/manual/html_node/Shell-Builtin-Commands.html
        'break cd continue eval exec exit export getopts hash pwd readonly return shift test times ' +
        'trap umask unset ' +
        // Bash built-ins
        'alias bind builtin caller command declare echo enable help let local logout mapfile printf ' +
        'read readarray source type typeset ulimit unalias ' +
        // Shell modifiers
        'set shopt ' +
        // Zsh built-ins
        'autoload bg bindkey bye cap chdir clone comparguments compcall compctl compdescribe compfiles ' +
        'compgroups compquote comptags comptry compvalues dirs disable disown echotc echoti emulate ' +
        'fc fg float functions getcap getln history integer jobs kill limit log noglob popd print ' +
        'pushd pushln rehash sched setcap setopt stat suspend ttyctl unfunction unhash unlimit ' +
        'unsetopt vared wait whence where which zcompile zformat zftp zle zmodload zparseopts zprof ' +
        'zpty zregexparse zsocket zstyle ztcp',
      _:
        '-ne -eq -lt -gt -f -d -e -s -l -a' // relevance booster
    },
    contains: [
      {
        className: 'meta',
        begin: /^#![^\n]+sh\s*$/,
        relevance: 10
      },
      {
        className: 'function',
        begin: /\w[\w\d_]*\s*\(\s*\)\s*\{/,
        returnBegin: true,
        contains: [hljs.inherit(hljs.TITLE_MODE, {begin: /\w[\w\d_]*/})],
        relevance: 0
      },
      hljs.HASH_COMMENT_MODE,
      QUOTE_STRING,
      APOS_STRING,
      VAR
    ]
  };
}
<|endoftext|>


<commit_before>/*
Language: Less
Author:   Max Mikhailov <seven.phases.max@gmail.com>
Category: css
*/

function(hljs) {
  var IDENT_RE        = '[\\w-]+'; // yes, Less identifiers may begin with a digit
  var INTERP_IDENT_RE = '(' + IDENT_RE + '|@{' + IDENT_RE + '})+';

  /* Generic Modes */

  var RULES = [], VALUE = []; // forward def. for recursive modes

  var STRING_MODE = function(c) { return {
    // Less strings are not multiline (also include '~' for more consistent coloring of "escaped" strings)
    className: 'string', begin: '~?' + c + '.*?' + c
  };};

  var IDENT_MODE = function(name, begin, relevance) { return {
    className: name, begin: begin, relevance: relevance
  };};

  var FUNCT_MODE = function(name, ident, obj) {
    return hljs.inherit({
        className: name, begin: ident + '\\(', end: '\\(',
        returnBegin: true, excludeEnd: true, relevance: 0
    }, obj);
  };

  var PARENS_MODE = {
    // used only to properly balance nested parens inside mixin call, def. arg list
    begin: '\\(', end: '\\)', contains: VALUE, relevance: 0
  };

  // generic Less highlighter (used almost everywhere except selectors):
  VALUE.push(
    hljs.C_LINE_COMMENT_MODE,
    hljs.C_BLOCK_COMMENT_MODE,
    STRING_MODE("'"),
    STRING_MODE('"'),
    hljs.CSS_NUMBER_MODE, // fixme: it does not include dot for numbers like .5em :(
    IDENT_MODE('hexcolor', '#[0-9A-Fa-f]+\\b'),
    FUNCT_MODE('function', '(url|data-uri)', {
      starts: {className: 'string', end: '[\\)\\n]', excludeEnd: true}
    }),
    FUNCT_MODE('function', IDENT_RE),
    PARENS_MODE,
    IDENT_MODE('variable', '@@?' + IDENT_RE, 10),
    IDENT_MODE('variable', '@{'  + IDENT_RE + '}'),
    IDENT_MODE('built_in', '~?`[^`]*?`'), // inline javascript (or whatever host language) *multiline* string
    { // @media features (it’s here to not duplicate things in AT_RULE_MODE with extra PARENS_MODE overriding):
      className: 'attribute', begin: IDENT_RE + '\\s*:', end: ':', returnBegin: true, excludeEnd: true
    }
  );

  var VALUE_WITH_RULESETS = VALUE.concat({
    begin: '{', end: '}', contains: RULES,
  });

  var MIXIN_GUARD_MODE = {
    beginKeywords: 'when', endsWithParent: true,
    contains: [{beginKeywords: 'and not'}].concat(VALUE) // using this form to override VALUE’s 'function' match
  };

  /* Rule-Level Modes */

  var RULE_MODE = {
    className: 'attribute',
    begin: INTERP_IDENT_RE, end: ':', excludeEnd: true,
    contains: [hljs.C_LINE_COMMENT_MODE, hljs.C_BLOCK_COMMENT_MODE],
    illegal: /\S/,
    starts: {end: '[;}]', returnEnd: true, contains: VALUE, illegal: '[<=$]'}
  };

  var AT_RULE_MODE = {
    className: 'at_rule', // highlight only at-rule keyword
    begin: '@(import|media|charset|font-face|(-[a-z]+-)?keyframes|supports|document|namespace|page|viewport|host)\\b',
    starts: {end: '[;{}]', returnEnd: true, contains: VALUE, relevance: 0}
  };

  // variable definitions and calls
  var VAR_RULE_MODE = {
    className: 'variable',
    variants: [
      // using more strict pattern for higher relevance to increase chances of Less detection.
      // this is *the only* Less specific statement used in most of the sources, so...
      // (we’ll still often loose to the css-parser unless there's '//' comment,
      // simply because 1 variable just can't beat 99 properties :)
      {begin: '@' + IDENT_RE + '\\s*:', relevance: 15},
      {begin: '@' + IDENT_RE}
    ],
    starts: {end: '[;}]', returnEnd: true, contains: VALUE_WITH_RULESETS}
  };

  var SELECTOR_MODE = {
    // first parse unambiguous selectors (i.e. those not starting with tag)
    // then fall into the scary lookahead-discriminator variant.
    // this mode also handles mixin definitions and calls
    variants: [{
      begin: '[\\.#:&\\[]', end: '[;{}]'  // mixin calls end with ';'
      }, {
      begin: INTERP_IDENT_RE + '[^;]*{',
      end: '{'
    }],
    returnBegin: true,
    returnEnd:   true,
    illegal: '[<=\'$"]',
    contains: [
      hljs.C_LINE_COMMENT_MODE,
      hljs.C_BLOCK_COMMENT_MODE,
      MIXIN_GUARD_MODE,
      IDENT_MODE('keyword',  'all\\b'),
      IDENT_MODE('variable', '@{'  + IDENT_RE + '}'),     // otherwise it’s identified as tag
      IDENT_MODE('tag',       INTERP_IDENT_RE + '%?', 0), // '%' for more consistent coloring of @keyframes "tags"
      IDENT_MODE('id',       '#'   + INTERP_IDENT_RE),
      IDENT_MODE('class',    '\\.' + INTERP_IDENT_RE, 0),
      IDENT_MODE('keyword',  '&', 0),
      FUNCT_MODE('pseudo',   ':not'),
      FUNCT_MODE('keyword',  ':extend'),
      IDENT_MODE('pseudo',   '::?' + INTERP_IDENT_RE),
      {className: 'attr_selector', begin: '\\[', end: '\\]'},
      {begin: '\\(', end: '\\)', contains: VALUE_WITH_RULESETS}, // argument list of parametric mixins
      {begin: '!important'} // eat !important after mixin call or it will be colored as tag
    ]
  };

  RULES.push(
    hljs.C_LINE_COMMENT_MODE,
    hljs.C_BLOCK_COMMENT_MODE,
    AT_RULE_MODE,
    VAR_RULE_MODE,
    SELECTOR_MODE,
    RULE_MODE
  );

  return {
    case_insensitive: true,
    illegal: '[=>\'/<($"]',
    contains: RULES
  };
}
<commit_msg>Remove combinatorial explosion in Less.<commit_after>/*
Language: Less
Author:   Max Mikhailov <seven.phases.max@gmail.com>
Category: css
*/

function(hljs) {
  var IDENT_RE        = '[\\w-]+'; // yes, Less identifiers may begin with a digit
  var INTERP_IDENT_RE = '(' + IDENT_RE + '|@{' + IDENT_RE + '})';

  /* Generic Modes */

  var RULES = [], VALUE = []; // forward def. for recursive modes

  var STRING_MODE = function(c) { return {
    // Less strings are not multiline (also include '~' for more consistent coloring of "escaped" strings)
    className: 'string', begin: '~?' + c + '.*?' + c
  };};

  var IDENT_MODE = function(name, begin, relevance) { return {
    className: name, begin: begin, relevance: relevance
  };};

  var FUNCT_MODE = function(name, ident, obj) {
    return hljs.inherit({
        className: name, begin: ident + '\\(', end: '\\(',
        returnBegin: true, excludeEnd: true, relevance: 0
    }, obj);
  };

  var PARENS_MODE = {
    // used only to properly balance nested parens inside mixin call, def. arg list
    begin: '\\(', end: '\\)', contains: VALUE, relevance: 0
  };

  // generic Less highlighter (used almost everywhere except selectors):
  VALUE.push(
    hljs.C_LINE_COMMENT_MODE,
    hljs.C_BLOCK_COMMENT_MODE,
    STRING_MODE("'"),
    STRING_MODE('"'),
    hljs.CSS_NUMBER_MODE, // fixme: it does not include dot for numbers like .5em :(
    IDENT_MODE('hexcolor', '#[0-9A-Fa-f]+\\b'),
    FUNCT_MODE('function', '(url|data-uri)', {
      starts: {className: 'string', end: '[\\)\\n]', excludeEnd: true}
    }),
    FUNCT_MODE('function', IDENT_RE),
    PARENS_MODE,
    IDENT_MODE('variable', '@@?' + IDENT_RE, 10),
    IDENT_MODE('variable', '@{'  + IDENT_RE + '}'),
    IDENT_MODE('built_in', '~?`[^`]*?`'), // inline javascript (or whatever host language) *multiline* string
    { // @media features (it’s here to not duplicate things in AT_RULE_MODE with extra PARENS_MODE overriding):
      className: 'attribute', begin: IDENT_RE + '\\s*:', end: ':', returnBegin: true, excludeEnd: true
    }
  );

  var VALUE_WITH_RULESETS = VALUE.concat({
    begin: '{', end: '}', contains: RULES,
  });

  var MIXIN_GUARD_MODE = {
    beginKeywords: 'when', endsWithParent: true,
    contains: [{beginKeywords: 'and not'}].concat(VALUE) // using this form to override VALUE’s 'function' match
  };

  /* Rule-Level Modes */

  var RULE_MODE = {
    className: 'attribute',
    begin: INTERP_IDENT_RE, end: ':', excludeEnd: true,
    contains: [hljs.C_LINE_COMMENT_MODE, hljs.C_BLOCK_COMMENT_MODE],
    illegal: /\S/,
    starts: {end: '[;}]', returnEnd: true, contains: VALUE, illegal: '[<=$]'}
  };

  var AT_RULE_MODE = {
    className: 'at_rule', // highlight only at-rule keyword
    begin: '@(import|media|charset|font-face|(-[a-z]+-)?keyframes|supports|document|namespace|page|viewport|host)\\b',
    starts: {end: '[;{}]', returnEnd: true, contains: VALUE, relevance: 0}
  };

  // variable definitions and calls
  var VAR_RULE_MODE = {
    className: 'variable',
    variants: [
      // using more strict pattern for higher relevance to increase chances of Less detection.
      // this is *the only* Less specific statement used in most of the sources, so...
      // (we’ll still often loose to the css-parser unless there's '//' comment,
      // simply because 1 variable just can't beat 99 properties :)
      {begin: '@' + IDENT_RE + '\\s*:', relevance: 15},
      {begin: '@' + IDENT_RE}
    ],
    starts: {end: '[;}]', returnEnd: true, contains: VALUE_WITH_RULESETS}
  };

  var SELECTOR_MODE = {
    // first parse unambiguous selectors (i.e. those not starting with tag)
    // then fall into the scary lookahead-discriminator variant.
    // this mode also handles mixin definitions and calls
    variants: [{
      begin: '[\\.#:&\\[]', end: '[;{}]'  // mixin calls end with ';'
      }, {
      begin: INTERP_IDENT_RE + '[^;]*{',
      end: '{'
    }],
    returnBegin: true,
    returnEnd:   true,
    illegal: '[<=\'$"]',
    contains: [
      hljs.C_LINE_COMMENT_MODE,
      hljs.C_BLOCK_COMMENT_MODE,
      MIXIN_GUARD_MODE,
      IDENT_MODE('keyword',  'all\\b'),
      IDENT_MODE('variable', '@{'  + IDENT_RE + '}'),     // otherwise it’s identified as tag
      IDENT_MODE('tag',       INTERP_IDENT_RE + '%?', 0), // '%' for more consistent coloring of @keyframes "tags"
      IDENT_MODE('id',       '#'   + INTERP_IDENT_RE),
      IDENT_MODE('class',    '\\.' + INTERP_IDENT_RE, 0),
      IDENT_MODE('keyword',  '&', 0),
      FUNCT_MODE('pseudo',   ':not'),
      FUNCT_MODE('keyword',  ':extend'),
      IDENT_MODE('pseudo',   '::?' + INTERP_IDENT_RE),
      {className: 'attr_selector', begin: '\\[', end: '\\]'},
      {begin: '\\(', end: '\\)', contains: VALUE_WITH_RULESETS}, // argument list of parametric mixins
      {begin: '!important'} // eat !important after mixin call or it will be colored as tag
    ]
  };

  RULES.push(
    hljs.C_LINE_COMMENT_MODE,
    hljs.C_BLOCK_COMMENT_MODE,
    AT_RULE_MODE,
    VAR_RULE_MODE,
    SELECTOR_MODE,
    RULE_MODE
  );

  return {
    case_insensitive: true,
    illegal: '[=>\'/<($"]',
    contains: RULES
  };
}
<|endoftext|>