<commit_before>import _throttle from 'lodash-es/throttle';

import { drag as d3_drag } from 'd3-drag';
import { interpolateNumber as d3_interpolateNumber } from 'd3-interpolate';

import {
    select as d3_select,
    event as d3_event,
    selectAll as d3_selectAll
} from 'd3-selection';

import {
    osmEntity,
    osmNote
} from '../osm';

import {
    uiDataEditor,
    uiFeatureList,
    uiInspector,
    uiNoteEditor
} from './index';

import { textDirection } from '../util/locale';

export function uiSidebar(context) {
    var inspector = uiInspector(context);
    var dataEditor = uiDataEditor(context);
    var noteEditor = uiNoteEditor(context);
    var _current;
    var _wasData = false;
    var _wasNote = false;

    function sidebar(selection) {
        var container = d3_select('#id-container');
        var minWidth = 280;
        var sidebarWidth;
        var containerWidth;
        var dragOffset;

        var resizer = selection
            .append('div')
            .attr('id', 'sidebar-resizer');

        // Set the initial width constraints
        selection
            .style('min-width', minWidth + 'px')
            .style('max-width', '400px')
            .style('width', '33.3333%');

        resizer.call(d3_drag()
            .container(container.node())
            .on('start', function() {
                // offset from edge of sidebar-resizer
                dragOffset = d3_event.sourceEvent.offsetX - 1;

                sidebarWidth = selection.node().getBoundingClientRect().width;
                containerWidth = container.node().getBoundingClientRect().width;
                var widthPct = (sidebarWidth / containerWidth) * 100;
                selection
                    .style('width', widthPct + '%')    // lock in current width
                    .style('max-width', '85%');        // but allow larger widths

                resizer.classed('dragging', true);
            })
            .on('drag', function() {
                var isRTL = (textDirection === 'rtl');
                var xMarginProperty = isRTL ? 'margin-right' : 'margin-left';

                var x = d3_event.x - dragOffset;
                sidebarWidth = isRTL ? containerWidth - x : x;

                var isCollapsed = selection.classed('collapsed');
                var shouldCollapse = sidebarWidth < minWidth;

                selection.classed('collapsed', shouldCollapse);

                if (shouldCollapse) {
                    if (!isCollapsed) {
                        selection
                            .style(xMarginProperty, '-400px')
                            .style('width', '400px');

                        context.ui().onResize([sidebarWidth - d3_event.dx, 0]);
                    }

                } else {
                    var widthPct = (sidebarWidth / containerWidth) * 100;
                    selection
                        .style(xMarginProperty, null)
                        .style('width', widthPct + '%');

                    if (isCollapsed) {
                        context.ui().onResize([-sidebarWidth, 0]);
                    } else {
                        context.ui().onResize([-d3_event.dx, 0]);
                    }
                }
            })
            .on('end', function() {
                resizer.classed('dragging', false);
            })
        );

        var featureListWrap = selection
            .append('div')
            .attr('class', 'feature-list-pane')
            .call(uiFeatureList(context));

        var inspectorWrap = selection
            .append('div')
            .attr('class', 'inspector-hidden inspector-wrap fr');

        function hover(datum) {
            if (datum && datum.__featurehash__) {   // hovering on data
                _wasData = true;
                sidebar
                    .show(dataEditor.datum(datum));

                selection.selectAll('.sidebar-component')
                    .classed('inspector-hover', true);

            } else if (datum instanceof osmNote) {
                if (context.mode().id === 'drag-note') return;
                _wasNote = true;

                sidebar
                    .show(noteEditor.note(datum));

                selection.selectAll('.sidebar-component')
                    .classed('inspector-hover', true);

            } else if (!_current && (datum instanceof osmEntity)) {
                featureListWrap
                    .classed('inspector-hidden', true);

                inspectorWrap
                    .classed('inspector-hidden', false)
                    .classed('inspector-hover', true);

                if (inspector.entityID() !== datum.id || inspector.state() !== 'hover') {
                    inspector
                        .state('hover')
                        .entityID(datum.id);

                    inspectorWrap
                        .call(inspector);
                }

            } else if (!_current) {
                featureListWrap
                    .classed('inspector-hidden', false);
                inspectorWrap
                    .classed('inspector-hidden', true);
                inspector
                    .state('hide');

            } else if (_wasData || _wasNote) {
                _wasNote = false;
                _wasData = false;
                d3_selectAll('.note').classed('hover', false);
                sidebar.hide();
            }
        }

        sidebar.hover = _throttle(hover, 200);

        sidebar.intersects = function(extent) {
            var rect = selection.node().getBoundingClientRect();
            return extent.intersects([
                context.projection.invert([0, rect.height]),
                context.projection.invert([rect.width, 0])
            ]);
        };

        sidebar.select = function(id, newFeature) {
            if (!_current && id) {
                // uncollapse the sidebar
                if (selection.classed('collapsed')) {
                    var entity = context.entity(id);
                    var extent = entity.extent(context.graph());
                    sidebar.expand(sidebar.intersects(extent));
                }

                featureListWrap
                    .classed('inspector-hidden', true);

                inspectorWrap
                    .classed('inspector-hidden', false)
                    .classed('inspector-hover', false);

                if (inspector.entityID() !== id || inspector.state() !== 'select') {
                    inspector
                        .state('select')
                        .entityID(id)
                        .newFeature(newFeature);

                    inspectorWrap
                        .call(inspector);
                }

            } else if (!_current) {
                featureListWrap
                    .classed('inspector-hidden', false);
                inspectorWrap
                    .classed('inspector-hidden', true);
                inspector
                    .state('hide');
            }
        };

        sidebar.show = function(component, element) {
            featureListWrap
                .classed('inspector-hidden', true);
            inspectorWrap
                .classed('inspector-hidden', true);

            if (_current) _current.remove();
            _current = selection
                .append('div')
                .attr('class', 'sidebar-component')
                .call(component, element);
        };

        sidebar.hide = function() {
            featureListWrap
                .classed('inspector-hidden', false);
            inspectorWrap
                .classed('inspector-hidden', true);

            if (_current) _current.remove();
            _current = null;
        };

        sidebar.expand = function(moveMap) {
            if (selection.classed('collapsed')) {
                sidebar.toggle(moveMap);
            }
        };

        sidebar.collapse = function(moveMap) {
            if (!selection.classed('collapsed')) {
                sidebar.toggle(moveMap);
            }
        };

        sidebar.toggle = function(moveMap) {
            var e = d3_event;
            if (e && e.sourceEvent) {
                e.sourceEvent.preventDefault();
            } else if (e) {
                e.preventDefault();
            }

            // Don't allow sidebar to toggle when the user is in the walkthrough.
            if (context.inIntro()) return;

            var isCollapsed = selection.classed('collapsed');
            var isCollapsing = !isCollapsed;
            var xMarginProperty = textDirection === 'rtl' ? 'margin-right' : 'margin-left';

            sidebarWidth = selection.node().getBoundingClientRect().width;

            // switch from % to px
            selection.style('width', sidebarWidth + 'px');

            var startMargin, endMargin, lastMargin;
            if (isCollapsing) {
                startMargin = lastMargin = 0;
                endMargin = -sidebarWidth;
            } else {
                startMargin = lastMargin = -sidebarWidth;
                endMargin = 0;
            }

            selection.transition()
                .style(xMarginProperty, endMargin + 'px')
                .tween('panner', function() {
                    var i = d3_interpolateNumber(startMargin, endMargin);
                    return function(t) {
                        var dx = lastMargin - Math.round(i(t));
                        lastMargin = lastMargin - dx;
                        context.ui().onResize(moveMap ? undefined : [dx, 0]);
                    };
                })
                .on('end', function() {
                    selection.classed('collapsed', isCollapsing);

                    // switch back from px to %
                    if (!isCollapsing) {
                        var containerWidth = container.node().getBoundingClientRect().width;
                        var widthPct = (sidebarWidth / containerWidth) * 100;
                        selection
                            .style(xMarginProperty, null)
                            .style('width', widthPct + '%');
                    }
                });
        };

        // toggle the sidebar collapse when double-clicking the resizer
        resizer.on('dblclick', sidebar.toggle);
    }

    sidebar.hover = function() {};
    sidebar.hover.cancel = function() {};
    sidebar.intersects = function() {};
    sidebar.select = function() {};
    sidebar.show = function() {};
    sidebar.hide = function() {};
    sidebar.expand = function() {};
    sidebar.collapse = function() {};
    sidebar.toggle = function() {};

    return sidebar;
}
<commit_msg>Make sure when selecting to restore the sidebar to select mode (closes #5603)<commit_after>import _throttle from 'lodash-es/throttle';

import { drag as d3_drag } from 'd3-drag';
import { interpolateNumber as d3_interpolateNumber } from 'd3-interpolate';

import {
    select as d3_select,
    event as d3_event,
    selectAll as d3_selectAll
} from 'd3-selection';

import {
    osmEntity,
    osmNote
} from '../osm';

import {
    uiDataEditor,
    uiFeatureList,
    uiInspector,
    uiNoteEditor
} from './index';

import { textDirection } from '../util/locale';

export function uiSidebar(context) {
    var inspector = uiInspector(context);
    var dataEditor = uiDataEditor(context);
    var noteEditor = uiNoteEditor(context);
    var _current;
    var _wasData = false;
    var _wasNote = false;

    function sidebar(selection) {
        var container = d3_select('#id-container');
        var minWidth = 280;
        var sidebarWidth;
        var containerWidth;
        var dragOffset;

        var resizer = selection
            .append('div')
            .attr('id', 'sidebar-resizer');

        // Set the initial width constraints
        selection
            .style('min-width', minWidth + 'px')
            .style('max-width', '400px')
            .style('width', '33.3333%');

        resizer.call(d3_drag()
            .container(container.node())
            .on('start', function() {
                // offset from edge of sidebar-resizer
                dragOffset = d3_event.sourceEvent.offsetX - 1;

                sidebarWidth = selection.node().getBoundingClientRect().width;
                containerWidth = container.node().getBoundingClientRect().width;
                var widthPct = (sidebarWidth / containerWidth) * 100;
                selection
                    .style('width', widthPct + '%')    // lock in current width
                    .style('max-width', '85%');        // but allow larger widths

                resizer.classed('dragging', true);
            })
            .on('drag', function() {
                var isRTL = (textDirection === 'rtl');
                var xMarginProperty = isRTL ? 'margin-right' : 'margin-left';

                var x = d3_event.x - dragOffset;
                sidebarWidth = isRTL ? containerWidth - x : x;

                var isCollapsed = selection.classed('collapsed');
                var shouldCollapse = sidebarWidth < minWidth;

                selection.classed('collapsed', shouldCollapse);

                if (shouldCollapse) {
                    if (!isCollapsed) {
                        selection
                            .style(xMarginProperty, '-400px')
                            .style('width', '400px');

                        context.ui().onResize([sidebarWidth - d3_event.dx, 0]);
                    }

                } else {
                    var widthPct = (sidebarWidth / containerWidth) * 100;
                    selection
                        .style(xMarginProperty, null)
                        .style('width', widthPct + '%');

                    if (isCollapsed) {
                        context.ui().onResize([-sidebarWidth, 0]);
                    } else {
                        context.ui().onResize([-d3_event.dx, 0]);
                    }
                }
            })
            .on('end', function() {
                resizer.classed('dragging', false);
            })
        );

        var featureListWrap = selection
            .append('div')
            .attr('class', 'feature-list-pane')
            .call(uiFeatureList(context));

        var inspectorWrap = selection
            .append('div')
            .attr('class', 'inspector-hidden inspector-wrap fr');

        function hover(datum) {
            if (datum && datum.__featurehash__) {   // hovering on data
                _wasData = true;
                sidebar
                    .show(dataEditor.datum(datum));

                selection.selectAll('.sidebar-component')
                    .classed('inspector-hover', true);

            } else if (datum instanceof osmNote) {
                if (context.mode().id === 'drag-note') return;
                _wasNote = true;

                sidebar
                    .show(noteEditor.note(datum));

                selection.selectAll('.sidebar-component')
                    .classed('inspector-hover', true);

            } else if (!_current && (datum instanceof osmEntity)) {
                featureListWrap
                    .classed('inspector-hidden', true);

                inspectorWrap
                    .classed('inspector-hidden', false)
                    .classed('inspector-hover', true);

                if (inspector.entityID() !== datum.id || inspector.state() !== 'hover') {
                    inspector
                        .state('hover')
                        .entityID(datum.id);

                    inspectorWrap
                        .call(inspector);
                }

            } else if (!_current) {
                featureListWrap
                    .classed('inspector-hidden', false);
                inspectorWrap
                    .classed('inspector-hidden', true);
                inspector
                    .state('hide');

            } else if (_wasData || _wasNote) {
                _wasNote = false;
                _wasData = false;
                d3_selectAll('.note').classed('hover', false);
                sidebar.hide();
            }
        }

        sidebar.hover = _throttle(hover, 200);

        sidebar.intersects = function(extent) {
            var rect = selection.node().getBoundingClientRect();
            return extent.intersects([
                context.projection.invert([0, rect.height]),
                context.projection.invert([rect.width, 0])
            ]);
        };

        sidebar.select = function(id, newFeature) {
            sidebar.hide();

            if (id) {
                // uncollapse the sidebar
                if (selection.classed('collapsed')) {
                    var entity = context.entity(id);
                    var extent = entity.extent(context.graph());
                    sidebar.expand(sidebar.intersects(extent));
                }

                featureListWrap
                    .classed('inspector-hidden', true);

                inspectorWrap
                    .classed('inspector-hidden', false)
                    .classed('inspector-hover', false);

                if (inspector.entityID() !== id || inspector.state() !== 'select') {
                    inspector
                        .state('select')
                        .entityID(id)
                        .newFeature(newFeature);

                    inspectorWrap
                        .call(inspector);
                }

            } else {
                inspector
                    .state('hide');
            }
        };

        sidebar.show = function(component, element) {
            featureListWrap
                .classed('inspector-hidden', true);
            inspectorWrap
                .classed('inspector-hidden', true);

            if (_current) _current.remove();
            _current = selection
                .append('div')
                .attr('class', 'sidebar-component')
                .call(component, element);
        };

        sidebar.hide = function() {
            featureListWrap
                .classed('inspector-hidden', false);
            inspectorWrap
                .classed('inspector-hidden', true);

            if (_current) _current.remove();
            _current = null;
        };

        sidebar.expand = function(moveMap) {
            if (selection.classed('collapsed')) {
                sidebar.toggle(moveMap);
            }
        };

        sidebar.collapse = function(moveMap) {
            if (!selection.classed('collapsed')) {
                sidebar.toggle(moveMap);
            }
        };

        sidebar.toggle = function(moveMap) {
            var e = d3_event;
            if (e && e.sourceEvent) {
                e.sourceEvent.preventDefault();
            } else if (e) {
                e.preventDefault();
            }

            // Don't allow sidebar to toggle when the user is in the walkthrough.
            if (context.inIntro()) return;

            var isCollapsed = selection.classed('collapsed');
            var isCollapsing = !isCollapsed;
            var xMarginProperty = textDirection === 'rtl' ? 'margin-right' : 'margin-left';

            sidebarWidth = selection.node().getBoundingClientRect().width;

            // switch from % to px
            selection.style('width', sidebarWidth + 'px');

            var startMargin, endMargin, lastMargin;
            if (isCollapsing) {
                startMargin = lastMargin = 0;
                endMargin = -sidebarWidth;
            } else {
                startMargin = lastMargin = -sidebarWidth;
                endMargin = 0;
            }

            selection.transition()
                .style(xMarginProperty, endMargin + 'px')
                .tween('panner', function() {
                    var i = d3_interpolateNumber(startMargin, endMargin);
                    return function(t) {
                        var dx = lastMargin - Math.round(i(t));
                        lastMargin = lastMargin - dx;
                        context.ui().onResize(moveMap ? undefined : [dx, 0]);
                    };
                })
                .on('end', function() {
                    selection.classed('collapsed', isCollapsing);

                    // switch back from px to %
                    if (!isCollapsing) {
                        var containerWidth = container.node().getBoundingClientRect().width;
                        var widthPct = (sidebarWidth / containerWidth) * 100;
                        selection
                            .style(xMarginProperty, null)
                            .style('width', widthPct + '%');
                    }
                });
        };

        // toggle the sidebar collapse when double-clicking the resizer
        resizer.on('dblclick', sidebar.toggle);
    }

    sidebar.hover = function() {};
    sidebar.hover.cancel = function() {};
    sidebar.intersects = function() {};
    sidebar.select = function() {};
    sidebar.show = function() {};
    sidebar.hide = function() {};
    sidebar.expand = function() {};
    sidebar.collapse = function() {};
    sidebar.toggle = function() {};

    return sidebar;
}
<|endoftext|>


<commit_before>import _filter from 'lodash-es/filter';

import { dispatch as d3_dispatch } from 'd3-dispatch';
import { select as d3_select } from 'd3-selection';

import { t } from '../util/locale';
import { data } from '../../data';
import { svgIcon } from '../svg';
import { uiDisclosure } from '../ui';
import { utilDetect } from '../util/detect';
import { utilRebind } from '../util/rebind';

export function uiSuccess(context) {
    var detected = utilDetect();
    var dispatch = d3_dispatch('cancel');
    var MAXEVENTS = 2;
    var _changeset;
    var _location;

    function success(selection) {
        var header = selection
            .append('div')
            .attr('class', 'header fillL');

        header
            .append('button')
            .attr('class', 'fr')
            .on('click', function() { dispatch.call('cancel'); })
            .call(svgIcon('#icon-close'));

        header
            .append('h3')
            .text(t('success.just_edited'));

        var body = selection
            .append('div')
            .attr('class', 'body save-success fillL');

        var summary = body
            .append('div')
            .attr('class', 'save-summary');

        summary
            .append('h3')
            .text(t('success.thank_you' + (_location ? '_location' : ''), { where: _location }));

        summary
            .append('p')
            .text(t('success.help_html'))
            .append('a')
            .attr('class', 'details')
            .attr('target', '_blank')
            .attr('tabindex', -1)
            .attr('href', t('success.help_link_url'))
            .call(svgIcon('#icon-out-link', 'inline'))
            .append('span')
            .text(t('success.help_link_text'));

        var osm = context.connection();
        if (!osm) return;

        var changesetURL = osm.changesetURL(_changeset.id);

        var table = summary
            .append('table')
            .attr('class', 'summary-table');

        var row = table
            .append('tr')
            .attr('class', 'summary-row');

        row
            .append('td')
            .attr('class', 'cell-icon summary-icon')
            .append('a')
            .attr('target', '_blank')
            .attr('href', changesetURL)
            .append('svg')
            .attr('class', 'logo-small')
            .append('use')
            .attr('xlink:href', '#logo-osm');

        var summaryDetail = row
            .append('td')
            .attr('class', 'cell-detail summary-detail');

        summaryDetail
            .append('a')
            .attr('class', 'cell-detail summary-view-on-osm')
            .attr('target', '_blank')
            .attr('href', changesetURL)
            .text(t('success.view_on_osm'));

        summaryDetail
            .append('div')
            .text(t('success.changeset_id', { changeset_id: _changeset.id }));

        // Gather community polygon IDs intersecting the map..
        var matchFeatures = data.community.query(context.map().center(), true);
        var matchIDs = matchFeatures.map(function(feature) { return feature.id; });

        // Gather community resources that are either global or match a polygon.
        var matchResources = _filter(data.community.resources, function(v) {
            return !v.featureId || matchIDs.indexOf(v.featureId) !== -1;
        });

        if (matchResources.length) {
            // sort by size ascending
            matchResources.sort(function(a, b) {
                var aSize = Infinity;
                var bSize = Infinity;
                if (a.featureId) {
                    aSize = data.community.features[a.featureId].properties.area;
                }
                if (b.featureId) {
                    bSize = data.community.features[b.featureId].properties.area;
                }
                return aSize < bSize ? -1 : aSize > bSize ? 1 : 0;
            });

            body
                .call(showCommunityLinks, matchResources);
        }
    }

    function showCommunityLinks(selection, matchResources) {
        var communityLinks = selection
            .append('div')
            .attr('class', 'save-communityLinks');

        communityLinks
            .append('h3')
            .text(t('success.like_osm'));

        var table = communityLinks
            .append('table')
            .attr('class', 'community-table');

        var row = table.selectAll('.community-row')
            .data(matchResources);

        var rowEnter = row.enter()
            .append('tr')
            .attr('class', 'community-row');

        rowEnter
            .append('td')
            .attr('class', 'cell-icon community-icon')
            .append('a')
            .attr('target', '_blank')
            .attr('href', function(d) { return d.url; })
            .append('svg')
            .attr('class', 'logo-small')
            .append('use')
            .attr('xlink:href', function(d) { return '#community-' + d.type; });

        var communityDetail = rowEnter
            .append('td')
            .attr('class', 'cell-detail community-detail');

        communityDetail
            .each(showCommunityDetails);
    }

    function showCommunityDetails(d) {
        var selection = d3_select(this);
        var replacements = {
            url: linkify(d.url),
            signupUrl: linkify(d.signupUrl || d.url)
        };

        selection
            .append('div')
            .attr('class', 'community-name')
            .append('a')
            .attr('target', '_blank')
            .attr('href', d.url)
            .text(t('community.' + d.id + '.name'));

        selection
            .append('div')
            .attr('class', 'community-description')
            .html(t('community.' + d.id + '.description', replacements));

        if (d.extendedDescription || (d.languageCodes && d.languageCodes.length)) {
            selection
                .append('div')
                .call(uiDisclosure(context, 'community-more-' + d.id, false)
                    .expanded(false)
                    .updatePreference(false)
                    .title(t('success.more'))
                    .content(showMore)
                );
        }

        var nextEvents = (d.events || [])
            .map(function(event) {                  // add parsed date
                event.date = new Date(event.when);
                return event;
            })
            .filter(function(event) {               // date is valid and future (or today)
                var t = event.date.getTime();
                var now = (new Date()).setHours(0,0,0,0);
                return !isNaN(t) && t >= now;
            })
            .sort(function(a, b) {                  // sort by date ascending
                return a.date  b.date ? 1 : 0;
            })
            .slice(0, MAXEVENTS);                   // limit number of events shown

        if (nextEvents.length) {
            selection
                .append('div')
                .call(uiDisclosure(context, 'community-events-' + d.id, false)
                    .expanded(false)
                    .updatePreference(false)
                    .title(t('success.events'))
                    .content(showNextEvents)
                )
                .select('.hide-toggle')
                .append('span')
                .attr('class', 'badge-text')
                .text(nextEvents.length);
        }

        function showMore(selection) {
            var more = selection
                .append('div')
                .attr('class', 'community-more');

            if (d.extendedDescription) {
                more
                    .append('div')
                    .attr('class', 'community-extended-description')
                    .html(t('community.' + d.id + '.extendedDescription', replacements));
            }

            if (d.languageCodes && d.languageCodes.length) {
                more
                    .append('div')
                    .attr('class', 'community-languages')
                    .text(t('success.languages', { languages: d.languageCodes.join(', ') }));
            }
        }

        function showNextEvents(selection) {
            var events = selection
                .append('div')
                .attr('class', 'community-events');

            var item = events.selectAll('.community-event')
                .data(nextEvents);

            var itemEnter = item.enter()
                .append('div')
                .attr('class', 'community-event');

            itemEnter
                .append('div')
                .attr('class', 'community-event-name')
                .append('a')
                .attr('target', '_blank')
                .attr('href', function(d) { return d.url; })
                .text(function(d) { return d.name; });

            itemEnter
                .append('div')
                .attr('class', 'community-event-when')
                .text(function(d) {
                    var options = {
                        weekday: 'short', day: 'numeric', month: 'short', year: 'numeric'
                    };
                    if (d.date.getHours() || d.date.getMinutes()) {   // include time if it has one
                        options.hour = 'numeric';
                        options.minute = 'numeric';
                    }
                    return d.date.toLocaleString(detected.locale, options);
                });

            itemEnter
                .append('div')
                .attr('class', 'community-event-where')
                .text(function(d) { return d.where; });

            itemEnter
                .append('div')
                .attr('class', 'community-event-description')
                .text(function(d) { return d.description; });
        }

        function linkify(url) {
            return '' + url + '';
        }
    }

    success.changeset = function(_) {
        if (!arguments.length) return _changeset;
        _changeset = _;
        return success;
    };

    success.location = function(_) {
        if (!arguments.length) return _location;
        _location = _;
        return success;
    };

    return utilRebind(success, dispatch, 'on');
}
<commit_msg>Add support for translated i18n events<commit_after>import _filter from 'lodash-es/filter';

import { dispatch as d3_dispatch } from 'd3-dispatch';
import { select as d3_select } from 'd3-selection';

import { t } from '../util/locale';
import { data } from '../../data';
import { svgIcon } from '../svg';
import { uiDisclosure } from '../ui';
import { utilDetect } from '../util/detect';
import { utilRebind } from '../util/rebind';

export function uiSuccess(context) {
    var detected = utilDetect();
    var dispatch = d3_dispatch('cancel');
    var MAXEVENTS = 2;
    var _changeset;
    var _location;

    function success(selection) {
        var header = selection
            .append('div')
            .attr('class', 'header fillL');

        header
            .append('button')
            .attr('class', 'fr')
            .on('click', function() { dispatch.call('cancel'); })
            .call(svgIcon('#icon-close'));

        header
            .append('h3')
            .text(t('success.just_edited'));

        var body = selection
            .append('div')
            .attr('class', 'body save-success fillL');

        var summary = body
            .append('div')
            .attr('class', 'save-summary');

        summary
            .append('h3')
            .text(t('success.thank_you' + (_location ? '_location' : ''), { where: _location }));

        summary
            .append('p')
            .text(t('success.help_html'))
            .append('a')
            .attr('class', 'details')
            .attr('target', '_blank')
            .attr('tabindex', -1)
            .attr('href', t('success.help_link_url'))
            .call(svgIcon('#icon-out-link', 'inline'))
            .append('span')
            .text(t('success.help_link_text'));

        var osm = context.connection();
        if (!osm) return;

        var changesetURL = osm.changesetURL(_changeset.id);

        var table = summary
            .append('table')
            .attr('class', 'summary-table');

        var row = table
            .append('tr')
            .attr('class', 'summary-row');

        row
            .append('td')
            .attr('class', 'cell-icon summary-icon')
            .append('a')
            .attr('target', '_blank')
            .attr('href', changesetURL)
            .append('svg')
            .attr('class', 'logo-small')
            .append('use')
            .attr('xlink:href', '#logo-osm');

        var summaryDetail = row
            .append('td')
            .attr('class', 'cell-detail summary-detail');

        summaryDetail
            .append('a')
            .attr('class', 'cell-detail summary-view-on-osm')
            .attr('target', '_blank')
            .attr('href', changesetURL)
            .text(t('success.view_on_osm'));

        summaryDetail
            .append('div')
            .text(t('success.changeset_id', { changeset_id: _changeset.id }));

        // Gather community polygon IDs intersecting the map..
        var matchFeatures = data.community.query(context.map().center(), true);
        var matchIDs = matchFeatures.map(function(feature) { return feature.id; });

        // Gather community resources that are either global or match a polygon.
        var matchResources = _filter(data.community.resources, function(v) {
            return !v.featureId || matchIDs.indexOf(v.featureId) !== -1;
        });

        if (matchResources.length) {
            // sort by size ascending
            matchResources.sort(function(a, b) {
                var aSize = Infinity;
                var bSize = Infinity;
                if (a.featureId) {
                    aSize = data.community.features[a.featureId].properties.area;
                }
                if (b.featureId) {
                    bSize = data.community.features[b.featureId].properties.area;
                }
                return aSize < bSize ? -1 : aSize > bSize ? 1 : 0;
            });

            body
                .call(showCommunityLinks, matchResources);
        }
    }

    function showCommunityLinks(selection, matchResources) {
        var communityLinks = selection
            .append('div')
            .attr('class', 'save-communityLinks');

        communityLinks
            .append('h3')
            .text(t('success.like_osm'));

        var table = communityLinks
            .append('table')
            .attr('class', 'community-table');

        var row = table.selectAll('.community-row')
            .data(matchResources);

        var rowEnter = row.enter()
            .append('tr')
            .attr('class', 'community-row');

        rowEnter
            .append('td')
            .attr('class', 'cell-icon community-icon')
            .append('a')
            .attr('target', '_blank')
            .attr('href', function(d) { return d.url; })
            .append('svg')
            .attr('class', 'logo-small')
            .append('use')
            .attr('xlink:href', function(d) { return '#community-' + d.type; });

        var communityDetail = rowEnter
            .append('td')
            .attr('class', 'cell-detail community-detail');

        communityDetail
            .each(showCommunityDetails);
    }

    function showCommunityDetails(d) {
        var selection = d3_select(this);
        var communityID = d.id;
        var replacements = {
            url: linkify(d.url),
            signupUrl: linkify(d.signupUrl || d.url)
        };

        selection
            .append('div')
            .attr('class', 'community-name')
            .append('a')
            .attr('target', '_blank')
            .attr('href', d.url)
            .text(t('community.' + d.id + '.name'));

        selection
            .append('div')
            .attr('class', 'community-description')
            .html(t('community.' + d.id + '.description', replacements));

        if (d.extendedDescription || (d.languageCodes && d.languageCodes.length)) {
            selection
                .append('div')
                .call(uiDisclosure(context, 'community-more-' + d.id, false)
                    .expanded(false)
                    .updatePreference(false)
                    .title(t('success.more'))
                    .content(showMore)
                );
        }

        var nextEvents = (d.events || [])
            .map(function(event) {                  // add parsed date
                event.date = new Date(event.when);
                return event;
            })
            .filter(function(event) {               // date is valid and future (or today)
                var t = event.date.getTime();
                var now = (new Date()).setHours(0,0,0,0);
                return !isNaN(t) && t >= now;
            })
            .sort(function(a, b) {                  // sort by date ascending
                return a.date  b.date ? 1 : 0;
            })
            .slice(0, MAXEVENTS);                   // limit number of events shown

        if (nextEvents.length) {
            selection
                .append('div')
                .call(uiDisclosure(context, 'community-events-' + d.id, false)
                    .expanded(false)
                    .updatePreference(false)
                    .title(t('success.events'))
                    .content(showNextEvents)
                )
                .select('.hide-toggle')
                .append('span')
                .attr('class', 'badge-text')
                .text(nextEvents.length);
        }

        function showMore(selection) {
            var more = selection
                .append('div')
                .attr('class', 'community-more');

            if (d.extendedDescription) {
                more
                    .append('div')
                    .attr('class', 'community-extended-description')
                    .html(t('community.' + d.id + '.extendedDescription', replacements));
            }

            if (d.languageCodes && d.languageCodes.length) {
                more
                    .append('div')
                    .attr('class', 'community-languages')
                    .text(t('success.languages', { languages: d.languageCodes.join(', ') }));
            }
        }

        function showNextEvents(selection) {
            var events = selection
                .append('div')
                .attr('class', 'community-events');

            var item = events.selectAll('.community-event')
                .data(nextEvents);

            var itemEnter = item.enter()
                .append('div')
                .attr('class', 'community-event');

            itemEnter
                .append('div')
                .attr('class', 'community-event-name')
                .append('a')
                .attr('target', '_blank')
                .attr('href', function(d) { return d.url; })
                .text(function(d) {
                    var name = d.name;
                    if (d.i18n && d.id) {
                        name = t('community.' + communityID + '.events.' + d.id + '.name', { default: name });
                    }
                    return name;
                });

            itemEnter
                .append('div')
                .attr('class', 'community-event-when')
                .text(function(d) {
                    var options = {
                        weekday: 'short', day: 'numeric', month: 'short', year: 'numeric'
                    };
                    if (d.date.getHours() || d.date.getMinutes()) {   // include time if it has one
                        options.hour = 'numeric';
                        options.minute = 'numeric';
                    }
                    return d.date.toLocaleString(detected.locale, options);
                });

            itemEnter
                .append('div')
                .attr('class', 'community-event-where')
                .text(function(d) {
                    var where = d.where;
                    if (d.i18n && d.id) {
                        where = t('community.' + communityID + '.events.' + d.id + '.where', { default: where });
                    }
                    return where;
                });

            itemEnter
                .append('div')
                .attr('class', 'community-event-description')
                .text(function(d) {
                    var description = d.description;
                    if (d.i18n && d.id) {
                        description = t('community.' + communityID + '.events.' + d.id + '.description', { default: description });
                    }
                    return description;
                });
        }

        function linkify(url) {
            return '' + url + '';
        }
    }

    success.changeset = function(_) {
        if (!arguments.length) return _changeset;
        _changeset = _;
        return success;
    };

    success.location = function(_) {
        if (!arguments.length) return _location;
        _location = _;
        return success;
    };

    return utilRebind(success, dispatch, 'on');
}
<|endoftext|>


<commit_before>var mongoose = require('mongoose');
// var dbUrl = 'mongodb://username:password@mongodb:port/database';
// var dbUrl = process.env.MONGO_DB_URL; // AWS
// var dbUrl = process.env.MONGODB_URI; // Heroku
var dbUrl = 'mongodb://localhost/personalblog';

mongoose.connect(dbUrl, { safe: true });

process.on('SIGINT', function() {
  mongoose.connection.close(function () {
    console.log('Mongoose default connection disconnected');
    process.exit(0);
  });
});

require('./models/user');
require('./models/post');
<commit_msg>Update MongoDB URL<commit_after>var mongoose = require('mongoose');
// var dbUrl = 'mongodb://username:password@mongodb:port/database';
// var dbUrl = process.env.MONGO_DB_URL; // AWS
// var dbUrl = process.env.MONGODB_URI; // Heroku
// var dbUrl = 'mongodb://localhost/personalblog';
var dbUrl = process.env.MONGODB_URI || 'mongodb://localhost/personalblog';

mongoose.connect(dbUrl, { safe: true });

process.on('SIGINT', function() {
  mongoose.connection.close(function () {
    console.log('Mongoose default connection disconnected');
    process.exit(0);
  });
});

require('./models/user');
require('./models/post');
<|endoftext|>


<commit_before>// -------------------------------------------------------------------------- \\
// File: ButtonView.js                                                        \\
// Module: ControlViews                                                       \\
// Requires: Core, Foundation, DOM, View, AbstractControlView.js              \\
// Author: Neil Jenkins                                                       \\
// License: © 2010–2013 Opera Software ASA. All rights reserved.              \\
// -------------------------------------------------------------------------- \\

"use strict";

( function ( NS ) {

/**
    Class: O.ButtonView

    Extends: O.AbstractControlView

    A ButtonView represents an interactive rectangle in your user interface
    which the user can click/tap to perform an action. The ButtonView uses a
    <button> element in the DOM by default. If the action being perfomed is
    actually a navigation and just shows/hides content and does not change any
    state, semantically you should change the layer tag to an .

    ### Using O.ButtonView ###

    The most common way to use O.ButtonView is to create an instance as part of
    the <O.View#draw method of your view class. For example:

        var Element = O.Element,
            el = Element.create;

        Element.appendChildren( layer, [
            el( 'h1', [
                'Which pill will you take?'
            ]),
            el( 'div.actions', [
                new O.ButtonView({
                    label: 'The Red Pill',
                    type: 'destructive size13',
                    icon: 'redpill',
                    isDisabled: O.bind( 'isNeo', controller ),
                    target: controller,
                    method: 'abort'
                }),
                new O.ButtonView({
                    label: 'The Blue Pill',
                    type: 'constructive size13',
                    icon: 'bluepill',
                    target: controller,
                    method: 'proceed'
                })
            ])
        ]);

    new O.ButtonView

    ### Styling O.ButtonView ###

    The underlying DOM structure is:

        <button class="ButtonView ${view.type}">

            ${view.label}
        </button>

    If there is no icon property set, the  will have a class of 'hidden'
    instead. The icon can be drawn as a background to the empty  element.
*/
var ButtonView = NS.Class({

    Extends: NS.AbstractControlView,

    /**
        Property: O.ButtonView#isActive
        Type: Boolean
        Default: false

        If the button is a toggle (like in the case of <O.MenuButtonView>, where
        the menu is either visible or not), this property should be set to true
        when in the active state, and false when not. This provides a CSS hook
        for drawing the correct style to represent the button state.

        <O.MenuButtonView> instances will automatically set this property
        correctly, but if you subclass O.ButtonView yourself in a similar way,
        be sure to set this when the state changes.
    */
    isActive: false,

    /**
        Property: O.ButtonView#type
        Type: String
        Default: ''

        A space-separated list of CSS classnames to give the layer in the DOM,
        irrespective of state.
    */
    type: '',

    /**
        Property: O.ButtonView#type
        Type: String
        Default: ''

        Set to the name of the icon to use, if any, for the button. See the
        general notes on using <O.ButtonView> for more information.
    */
    icon: '',

    /**
        Property: O.ButtonView#tabIndex
        Type: Number
        Default: -1

        Overrides default in <O.AbstractControlView#tabIndex>.
    */
    tabIndex: -1,

    // --- Render ---

    /**
        Property: O.ButtonView#layerTag
        Type: String
        Default: 'button'

        Overrides default in <O.View#layerTag>.
    */
    layerTag: 'button',

    /**
        Property: O.ButtonView#className
        Type: String

        Overrides default in <O.View#className>. The layer will always have the
        class "ButtonView" plus any classes listed in the <O.ButtonView#type>
        property. In addition, it may have the following classes depending on
        the state:

        hasShortcut - If the view has a shortcut property set.
        active      - If the view's isActive property is true.
        disabled    - If the view's isDisabled property is true.
    */
    className: function () {
        var type = this.get( 'type' );
        return 'ButtonView' +
            ( type ? ' ' + type : '' ) +
            ( this.get( 'shortcut' ) ? ' hasShortcut' : '' ) +
            ( this.get( 'isActive' ) ? ' active' : '' ) +
            ( this.get( 'isDisabled' ) ? ' disabled' : '' );
    }.property( 'type', 'shortcut', 'isActive', 'isDisabled' ),

    /**
        Method: O.ButtonView#draw

        Overridden to draw view. See <O.View#draw>. For DOM structure, see
        general <O.ButtonView> notes.
    */
    draw: function ( layer ) {
        layer.appendChild(
            NS.Element.create( 'i', {
                className: this.get( 'icon' ) || 'hidden'
            })
        );
        this._domControl = layer;
        ButtonView.parent.draw.call( this, layer );
    },

    // --- Keep render in sync with state ---

    /**
        Method: O.ButtonView#buttonNeedsRedraw

        Calls <O.View#propertyNeedsRedraw> for extra properties requiring
        redraw.
    */
    buttonNeedsRedraw: function ( self, property, oldValue ) {
       return this.propertyNeedsRedraw( self, property, oldValue );
    }.observes( 'icon' ),

    /**
        Method: O.ButtonView#redrawIcon

        Updates the className of the  representing the button's icon.
    */
    redrawIcon: function ( layer ) {
        layer.firstChild.className = this.get( 'icon' ) || 'hidden';
    },

    // --- Activate ---

    /**
        Property: O.ButtonView#target
        Type: Object|null
        Default: null

        The object to fire an event/call a method on when the button is
        activated. If null (the default), the ButtonView instance itself will be
        used.
    */
    target: null,

    /**
        Property: O.ButtonView#action
        Type: String|null
        Default: null

        The name of the event to fire on the <#target> when the button is
        activated. Note, you should set *either* the action property or the
        <#method> property. If both are set, the method property will be
        ignored.
    */
    action: null,

    /**
        Property: O.ButtonView#method
        Type: String|null
        Default: null

        The name of the method to call on the <#target> when the button is
        activated. Note, you should set *either* the <#action> property or the
        method property. If both are set, the method property will be ignored.
    */
    method: null,

    /**
        Method: O.ButtonView#activate

        This method is called when the button is triggered, either by being
        clicked/tapped on, or via a keyboard shortcut. If the button is
        disabled, it will do nothing. Otherwise, it fires an event with the name
        given in the <#action> property on the <#target> object. Or, if no
        action is defined, calls the method named in the <#method> property on
        the object instead.

        If an event is fired, the `originView` property of the event object
        provides a reference back to the button that fired it. If a method is
        called, the ButtonView instance will be passed as the sole argument.

        It also fires an event called `button:activate` on itself.
    */
    activate: function () {
        if ( !this.get( 'isDisabled' ) ) {
            var target = this.get( 'target' ) || this,
                action;
            if ( action = this.get( 'action' ) ) {
                target.fire( action, { originView: this } );
            } else if ( action = this.get( 'method' ) ) {
                target[ action ]( this );
            }
            this.fire( 'button:activate' );
        }
    },

    // --- Keep state in sync with render ---

    /**
        Property (private): O.ButtonView#_ignore
        Type: Boolean

        If true, don't activate on click/mouseup.

        We want to trigger on mouseup so that the button can be used in a menu
        in a single click action. However, we also want to trigger on click for
        accessibility reasons. We don't want to trigger twice though, and at the
        time of the mouseup event there's no way to know if a click event will
        follow it. However, if a click event *is* following it, in most
        browsers, the click event will already be in the event queue, so we
        temporarily ignore clicks and put a callback function onto the end of
        the event queue to stop ignoring them. This will only run after the
        click event has fired (if there is one). The exception is Opera, where
        it gets queued before the click event. By adding a small 200ms delay we
        can more or less guarantee it is queued after, and it also prevents
        double click from activating the button twice, which could have
        unintended effects.
    */
    _ignore: false,

    /**
        Method (private): O.ButtonView#_monitorClicks

        Start activating on click/mouseup again.
    */
    _monitorClicks: function () {
        this._ignore = false;
    },

    /**
        Method (private): O.ButtonView#_activateOnClick

        Activates the button on normal clicks.

        Parameters:
            event - {Event} The click or mouseup event.
    */
    _activateOnClick: function ( event ) {
        if ( this._ignore || event.button || event.metaKey || event.ctrlKey ) {
            return;
        }
        if ( event.type !== 'mouseup' || this.getParent( NS.MenuView ) ) {
            this._ignore = true;
            NS.RunLoop.invokeAfterDelay( this._monitorClicks, 200, this );
            this.activate();
        }
    }.on( 'mouseup', 'click' ),

    /**
        Method (private): O.ButtonView#_activateOnEnter

        Activates the button when it has keyboard focus and the `enter` key is
        pressed.

        Parameters:
            event - {Event} The keypress event.
    */
    _activateOnEnter: function ( event ) {
        if ( NS.DOMEvent.lookupKey( event ) === 'enter' ) {
            this.activate();
            // Don't want to trigger global keyboard shortcuts
            event.stopPropagation();
        }
    }.on( 'keypress' )
});

NS.ButtonView = ButtonView;

}( this.O ) );
<commit_msg>Prevent default on button press.<commit_after>// -------------------------------------------------------------------------- \\
// File: ButtonView.js                                                        \\
// Module: ControlViews                                                       \\
// Requires: Core, Foundation, DOM, View, AbstractControlView.js              \\
// Author: Neil Jenkins                                                       \\
// License: © 2010–2013 Opera Software ASA. All rights reserved.              \\
// -------------------------------------------------------------------------- \\

"use strict";

( function ( NS ) {

/**
    Class: O.ButtonView

    Extends: O.AbstractControlView

    A ButtonView represents an interactive rectangle in your user interface
    which the user can click/tap to perform an action. The ButtonView uses a
    <button> element in the DOM by default. If the action being perfomed is
    actually a navigation and just shows/hides content and does not change any
    state, semantically you should change the layer tag to an .

    ### Using O.ButtonView ###

    The most common way to use O.ButtonView is to create an instance as part of
    the <O.View#draw method of your view class. For example:

        var Element = O.Element,
            el = Element.create;

        Element.appendChildren( layer, [
            el( 'h1', [
                'Which pill will you take?'
            ]),
            el( 'div.actions', [
                new O.ButtonView({
                    label: 'The Red Pill',
                    type: 'destructive size13',
                    icon: 'redpill',
                    isDisabled: O.bind( 'isNeo', controller ),
                    target: controller,
                    method: 'abort'
                }),
                new O.ButtonView({
                    label: 'The Blue Pill',
                    type: 'constructive size13',
                    icon: 'bluepill',
                    target: controller,
                    method: 'proceed'
                })
            ])
        ]);

    new O.ButtonView

    ### Styling O.ButtonView ###

    The underlying DOM structure is:

        <button class="ButtonView ${view.type}">

            ${view.label}
        </button>

    If there is no icon property set, the  will have a class of 'hidden'
    instead. The icon can be drawn as a background to the empty  element.
*/
var ButtonView = NS.Class({

    Extends: NS.AbstractControlView,

    /**
        Property: O.ButtonView#isActive
        Type: Boolean
        Default: false

        If the button is a toggle (like in the case of <O.MenuButtonView>, where
        the menu is either visible or not), this property should be set to true
        when in the active state, and false when not. This provides a CSS hook
        for drawing the correct style to represent the button state.

        <O.MenuButtonView> instances will automatically set this property
        correctly, but if you subclass O.ButtonView yourself in a similar way,
        be sure to set this when the state changes.
    */
    isActive: false,

    /**
        Property: O.ButtonView#type
        Type: String
        Default: ''

        A space-separated list of CSS classnames to give the layer in the DOM,
        irrespective of state.
    */
    type: '',

    /**
        Property: O.ButtonView#type
        Type: String
        Default: ''

        Set to the name of the icon to use, if any, for the button. See the
        general notes on using <O.ButtonView> for more information.
    */
    icon: '',

    /**
        Property: O.ButtonView#tabIndex
        Type: Number
        Default: -1

        Overrides default in <O.AbstractControlView#tabIndex>.
    */
    tabIndex: -1,

    // --- Render ---

    /**
        Property: O.ButtonView#layerTag
        Type: String
        Default: 'button'

        Overrides default in <O.View#layerTag>.
    */
    layerTag: 'button',

    /**
        Property: O.ButtonView#className
        Type: String

        Overrides default in <O.View#className>. The layer will always have the
        class "ButtonView" plus any classes listed in the <O.ButtonView#type>
        property. In addition, it may have the following classes depending on
        the state:

        hasShortcut - If the view has a shortcut property set.
        active      - If the view's isActive property is true.
        disabled    - If the view's isDisabled property is true.
    */
    className: function () {
        var type = this.get( 'type' );
        return 'ButtonView' +
            ( type ? ' ' + type : '' ) +
            ( this.get( 'shortcut' ) ? ' hasShortcut' : '' ) +
            ( this.get( 'isActive' ) ? ' active' : '' ) +
            ( this.get( 'isDisabled' ) ? ' disabled' : '' );
    }.property( 'type', 'shortcut', 'isActive', 'isDisabled' ),

    /**
        Method: O.ButtonView#draw

        Overridden to draw view. See <O.View#draw>. For DOM structure, see
        general <O.ButtonView> notes.
    */
    draw: function ( layer ) {
        layer.appendChild(
            NS.Element.create( 'i', {
                className: this.get( 'icon' ) || 'hidden'
            })
        );
        this._domControl = layer;
        ButtonView.parent.draw.call( this, layer );
    },

    // --- Keep render in sync with state ---

    /**
        Method: O.ButtonView#buttonNeedsRedraw

        Calls <O.View#propertyNeedsRedraw> for extra properties requiring
        redraw.
    */
    buttonNeedsRedraw: function ( self, property, oldValue ) {
       return this.propertyNeedsRedraw( self, property, oldValue );
    }.observes( 'icon' ),

    /**
        Method: O.ButtonView#redrawIcon

        Updates the className of the  representing the button's icon.
    */
    redrawIcon: function ( layer ) {
        layer.firstChild.className = this.get( 'icon' ) || 'hidden';
    },

    // --- Activate ---

    /**
        Property: O.ButtonView#target
        Type: Object|null
        Default: null

        The object to fire an event/call a method on when the button is
        activated. If null (the default), the ButtonView instance itself will be
        used.
    */
    target: null,

    /**
        Property: O.ButtonView#action
        Type: String|null
        Default: null

        The name of the event to fire on the <#target> when the button is
        activated. Note, you should set *either* the action property or the
        <#method> property. If both are set, the method property will be
        ignored.
    */
    action: null,

    /**
        Property: O.ButtonView#method
        Type: String|null
        Default: null

        The name of the method to call on the <#target> when the button is
        activated. Note, you should set *either* the <#action> property or the
        method property. If both are set, the method property will be ignored.
    */
    method: null,

    /**
        Method: O.ButtonView#activate

        This method is called when the button is triggered, either by being
        clicked/tapped on, or via a keyboard shortcut. If the button is
        disabled, it will do nothing. Otherwise, it fires an event with the name
        given in the <#action> property on the <#target> object. Or, if no
        action is defined, calls the method named in the <#method> property on
        the object instead.

        If an event is fired, the `originView` property of the event object
        provides a reference back to the button that fired it. If a method is
        called, the ButtonView instance will be passed as the sole argument.

        It also fires an event called `button:activate` on itself.
    */
    activate: function () {
        if ( !this.get( 'isDisabled' ) ) {
            var target = this.get( 'target' ) || this,
                action;
            if ( action = this.get( 'action' ) ) {
                target.fire( action, { originView: this } );
            } else if ( action = this.get( 'method' ) ) {
                target[ action ]( this );
            }
            this.fire( 'button:activate' );
        }
    },

    // --- Keep state in sync with render ---

    /**
        Property (private): O.ButtonView#_ignore
        Type: Boolean

        If true, don't activate on click/mouseup.

        We want to trigger on mouseup so that the button can be used in a menu
        in a single click action. However, we also want to trigger on click for
        accessibility reasons. We don't want to trigger twice though, and at the
        time of the mouseup event there's no way to know if a click event will
        follow it. However, if a click event *is* following it, in most
        browsers, the click event will already be in the event queue, so we
        temporarily ignore clicks and put a callback function onto the end of
        the event queue to stop ignoring them. This will only run after the
        click event has fired (if there is one). The exception is Opera, where
        it gets queued before the click event. By adding a small 200ms delay we
        can more or less guarantee it is queued after, and it also prevents
        double click from activating the button twice, which could have
        unintended effects.
    */
    _ignore: false,

    /**
        Method (private): O.ButtonView#_monitorClicks

        Start activating on click/mouseup again.
    */
    _monitorClicks: function () {
        this._ignore = false;
    },

    /**
        Method (private): O.ButtonView#_activateOnClick

        Activates the button on normal clicks.

        Parameters:
            event - {Event} The click or mouseup event.
    */
    _activateOnClick: function ( event ) {
        if ( this._ignore || event.button || event.metaKey || event.ctrlKey ) {
            return;
        }
        if ( event.type !== 'mouseup' || this.getParent( NS.MenuView ) ) {
            this._ignore = true;
            NS.RunLoop.invokeAfterDelay( this._monitorClicks, 200, this );
            this.activate();
            event.preventDefault();
        }
    }.on( 'mouseup', 'click' ),

    /**
        Method (private): O.ButtonView#_activateOnEnter

        Activates the button when it has keyboard focus and the `enter` key is
        pressed.

        Parameters:
            event - {Event} The keypress event.
    */
    _activateOnEnter: function ( event ) {
        if ( NS.DOMEvent.lookupKey( event ) === 'enter' ) {
            this.activate();
            // Don't want to trigger global keyboard shortcuts
            event.stopPropagation();
        }
    }.on( 'keypress' )
});

NS.ButtonView = ButtonView;

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


<commit_before>// -------------------------------------------------------------------------- \\
// File: LightboxView.js                                                      \\
// Module: View                                                               \\
// Requires: Core, Foundation, DOM, View.js                                   \\
// Author: Neil Jenkins                                                       \\
// License: © 2010–2012 Opera Software ASA. All rights reserved.              \\
// -------------------------------------------------------------------------- \\

/*global location*/

"use strict";

( function ( NS ) {

var LightboxItemView = NS.Class({

    Extends: NS.View,

    Mixin: NS.AnimatableView,

    lightbox: null,
    index: 0,
    activeIndex: -1,

    frameThickness: 30,

    position: function () {
        return this.get( 'index' ) - this.get( 'activeIndex' );
    }.property( 'activeIndex' ),

    isActive: NS.bind( 'lightbox.isActive', null, NS.Transform.toBoolean ),
    hideControls: true,

    className: function () {
        return 'LightboxItemView' +
            ( this.get( 'hideControls' ) ? ' hideControls' : '' );
    }.property( 'hideControls' ),

    positioning: 'absolute',

    draw: function ( layer ) {
        var Element = NS.Element,
            el = Element.create;

        Element.appendChildren( layer, [
            this.renderContent(),
            this._close = el( 'a.close', { href: location.href }, [
                el( 'span.navLink', [ NS.loc( 'Close' ) ] )
            ])
        ]);
    },

    updateDrawProperties: function () {
        var position = this.get( 'position' ),
            layout =
                !position ?
                    this.get( 'isActive' ) ? 'centreLayout' : 'thumbLayout' :
                position > 0 ?
                    'rightLayout' : 'leftLayout';

        this.set( 'animateLayerDuration', layout === 'thumbLayout' ? 350: 500 )
            .set( 'hideControls', !!position )
            .set( 'layout', this.get( layout ) );
    }.observes( 'isActive', 'position', 'dimensions' ),

    // --- Animation ---

    didAnimate: function () {
        this.increment( 'animating', -1 );
        if ( !this.get( 'position' ) && !this.get( 'isActive' ) ) {
            this.get( 'lightbox' ).removeFromDocument();
        }
    },

    // --- Layout ---

    thumbPosition: {
        top: 0,
        right: 0,
        bottom: 0,
        left: 0
    },

    contentDimensions: {
        width: 300,
        height: 300
    },

    didAppendLayerToDocument: function () {
        // Must flush CSS changes -> force reflow/redraw.
        if ( !this.get( 'position' ) ) {
            this.get( 'layer' ).getBoundingClientRect();
        }
        return LightboxItemView.parent.didAppendLayerToDocument.call( this );
    },

    parentViewDidResize: function () {
        this.computedPropertyDidChange( 'dimensions' );
    },

    dimensions: function () {
        var content = this.get( 'contentDimensions' ),
            width = content.width,
            height = content.height,
            frame = this.get( 'frameThickness' ),

            lightbox = this.get( 'lightbox' ),
            lightboxWidth = lightbox.get( 'pxWidth' ),
            lightboxHeight = lightbox.get( 'pxHeight' ),

            // 75 is the minimum gap to the edge of the screen.
            maxWidth = Math.min( width, lightboxWidth - frame - 75 ),
            maxHeight = Math.min( height, lightboxHeight - frame - 75 ),

            ratio = Math.max( width / maxWidth, height / maxHeight );

        if ( ratio !== 1 ) {
            width = parseInt( width / ratio, 10 );
            height = parseInt( height / ratio, 10 );
        }

        return {
            canvasWidth: lightboxWidth,
            canvasHeight: lightboxHeight,

            width: width,
            height: height
        };
    }.property( 'contentDimensions', 'frameThickness' ),

    thumbLayout: function () {
        var dimensions = this.get( 'dimensions' ),
            width = dimensions.width,
            height = dimensions.height,
            frame = this.get( 'frameThickness' ),
            thumb = this.get( 'thumbPosition' );

        return {
            width: width,
            height: height,
            // IE8 doesn't support bounds.(width|height)
            translateX: Math.floor( thumb.left +
                ( thumb.right - thumb.left - width - frame ) / 2 ),
            translateY: Math.floor( thumb.top +
                ( thumb.bottom - thumb.top - height - frame ) / 2 ),
            scale: ( thumb.right - thumb.left ) / ( width + frame )
        };
    }.property( 'dimensions', 'thumbPosition' ),

    centreLayout: function () {
        var dimensions = this.get( 'dimensions' ),
            width = dimensions.width,
            height = dimensions.height,
            frame = this.get( 'frameThickness' );

        return {
            width: width,
            height: height,
            translateX: ( dimensions.canvasWidth - width - frame ) >> 1,
            translateY: ( dimensions.canvasHeight - height - frame ) >> 1
        };
    }.property( 'dimensions' ),

    leftLayout: function () {
        var dimensions = this.get( 'dimensions' ),
            width = dimensions.width,
            height = dimensions.height,
            frame = this.get( 'frameThickness' );

        return {
            width: dimensions.width,
            height: dimensions.height,
            translateX: -( width + frame ),
            translateY: ( dimensions.canvasHeight - height - frame ) >> 1,
            scale: 0.5
        };
    }.property( 'dimensions' ),

    rightLayout: function () {
        var dimensions = this.get( 'dimensions' ),
            width = dimensions.width,
            height = dimensions.height,
            frame = this.get( 'frameThickness' );

        return {
            width: width,
            height: height,
            translateX: dimensions.canvasWidth,
            translateY: ( dimensions.canvasHeight - height - frame ) >> 1,
            scale: 0.5
        };
    }.property( 'dimensions' ),

    // --- Event handling ---

    close: function () {
        this.get( 'lightbox' ).close();
    },

    onClick: function ( event ) {
        if ( NS.Element.contains( this._close, event.target ) ) {
            event.preventDefault();
            this.close();
        }
        event.stopPropagation();
    }.on( 'click' )
});

var LightboxPhotoView = NS.Class({

    Extends: LightboxItemView,

    className: function () {
        return 'LightboxItemView LightboxPhotoView' +
            ( this.get( 'hideControls' ) ? ' hideControls' : '' );
    }.property( 'hideControls' ),

    didAnimate: function () {
        LightboxPhotoView.parent.didAnimate.call( this );
        if ( this._highResShowing && this.get( 'position' ) ) {
            this.switchToLowRes();
        }
    },

    thumbImage: function () {
        return NS.Element.create( 'img', {
            src: this.get( 'imgThumb' ).src,
            alt: ''
        });
    }.property(),

    getSrcForWidth: function ( width ) {
        var src = this.get( 'imgSrc' );
        return src + ( src.indexOf( '?' ) > -1 ? '&' : '?' ) +
            'width=' + width;
    },

    _highResShowing: false,

    highResLoaded: false,

    highResImage: function () {
        var img = NS.Element.create( 'img', { alt: '' } );
        img.onload = function () {
            this.set( 'highResLoaded', true );
            img = img.onload = null;
        }.invokeInRunLoop().bind( this );
        img.src =
            this.getSrcForWidth( this.get( 'centreLayout' ).width );
        return img;
    }.property(),

    switchToHighRes: function () {
        if ( this.get( 'isRendered' ) &&
                !this.get( 'position' ) &&
                !this._highResShowing ) {
            var img = this.get( 'highResImage' );
            if ( this.get( 'highResLoaded' ) ) {
                this.get( 'layer' ).replaceChild(
                    img, this.get( 'thumbImage' ) );
                this._highResShowing = true;
            }
        }
    }.queue( 'render' ).observes( 'position', 'highResLoaded' ),

    switchToLowRes: function () {
        this.get( 'layer' ).replaceChild(
            this.get( 'thumbImage' ), this.get( 'highResImage' ) );
        this._highResShowing = false;
    }.queue( 'render' ),

    renderContent: function () {
        if ( !this.get( 'position' ) ) {
            this.get( 'highResImage' );
        }
        return this.get( 'thumbImage' );
    },

    // --- Layout ---

    thumbPosition: function () {
        return this.get( 'imgThumb' ).getBoundingClientRect();
    }.property(),

    contentDimensions: function () {
        return {
            width: this.get( 'imgWidth' ),
            height: this.get( 'imgHeight' )
        };
    }.property()
});

var LightboxView = NS.Class({

    Extends: NS.View,

    isActive: false,

    init: function ( parentView, options ) {
        LightboxView.parent.init.call( this, options );
        this._rootView = parentView;
        this._shortcuts = NS.RootViewController.kbShortcuts;
    },

    index: function ( index ) {
        index = index ? index.mod( this.get( 'total' ) ) : 0;
        this.get( 'childViews' ).forEach( function ( view ) {
            view.set( 'activeIndex', index );
        });
        return index;
    }.property(),

    total: 0,

    className: function () {
        return 'LightboxView' +
            ( this.get( 'isActive' ) ? ' active' : '' ) +
            ( this.get( 'total' ) < 2 ? ' hideControls' : '' );
    }.property( 'isActive', 'total' ),

    positioning: 'absolute',
    layout: NS.View.LAYOUT_FILL_PARENT,

    draw: function ( layer ) {
        var Element = NS.Element,
            el = Element.create;

        Element.appendChildren( layer, [
            el( 'div.background' ),
            el( 'a.prev.navLink', { href: location.href }, [
                NS.loc( 'Previous' )
            ]),
            el( 'a.next.navLink', { href: location.href }, [
                NS.loc( 'Next' )
            ])
        ]);
        LightboxView.parent.draw.call( this, layer );
    },

    // add/remove

    add: function ( view, activeIndex ) {
        this.increment( 'total', 1 );

        if ( activeIndex !== undefined ) {
            this.set( 'index', activeIndex );
        }

        view.set( 'lightbox', this )
            .set( 'index', this.get( 'total' ) - 1 )
            .set( 'activeIndex', this.get( 'index' ) );

        this.insertView( view );
    },

    remove: function ( view ) {
        this.removeView( view )
            .increment( 'total', -1 )
            .increment( 'index', 0 );
    },

    // Open/close

    open: function ( lightboxViews, startIndex ) {
        // Get list of item views.
        if ( !( lightboxViews instanceof Array ) ) {
            lightboxViews = [ lightboxViews ];
        }

        // Set index/total
        this.set( 'total', lightboxViews.length )
            .set( 'index', startIndex || ( startIndex = 0 ) );

        var l = lightboxViews.length,
            view;
        while ( l-- ) {
            view = lightboxViews[l];
            view.set( 'lightbox', this )
                .set( 'index', l )
                .set( 'activeIndex', startIndex );

            this.insertView( view );
        }

        // Disable keyboard shortcuts
        this._kbEnabled = this._shortcuts.get( 'isEnabled' );
        this._shortcuts.set( 'isEnabled', false );

        // Insert view
        this._rootView.insertView( this );

        // Capture events
        NS.RootViewController.queueResponder( this );

        // Now, fade in gradient background and views.
        this.set( 'isActive', true );
    },

    close: function () {
        // Reenable keyboard shortcuts
        this._shortcuts.set( 'isEnabled', this._kbEnabled );

        // Stop capturing events
        NS.RootViewController.removeResponder( this );

        // Fade out gradient background and views.
        // Item view will call removeFromDocument after animation.
        this.set( 'isActive', false );
    },

    removeFromDocument: function () {
        this._rootView.removeView( this );

        var children = this.get( 'childViews' ),
            l = children.length,
            view;

        while ( l-- ) {
            view = children[l];
            this.removeView( view );
            view.set( 'lightbox', null );
        }
    }.queue( 'render' ),

    // Dimensions

    pxWidth: function () {
        return this._rootView.get( 'pxWidth' );
    }.property().nocache(),

    pxHeight: function () {
        return this._rootView.get( 'pxHeight' );
    }.property().nocache(),

    // Actions

    keyboardShortcuts: function ( event ) {
        switch ( NS.DOMEvent.lookupKey( event ) ) {
            case 'esc':
                this.close();
                break;
            case 'right':
                event.preventDefault();
                this.increment( 'index', 1 );
                break;
            case 'left':
                event.preventDefault();
                this.increment( 'index', -1 );
                break;
        }
        event.stopPropagation();
    }.on( 'keydown' ),

    onClick: function ( event ) {
        if ( event.button || event.metaKey || event.ctrlKey ) { return; }
        var action = event.target.className;
        if ( /prev/.test( action ) ) {
            this.increment( 'index', -1 );
        } else if ( /next/.test( action ) ) {
            this.increment( 'index', 1 );
        } else {
            this.close();
        }
        event.preventDefault();
        event.stopPropagation();
    }.on( 'click' )
});

NS.LightboxItemView = LightboxItemView;
NS.LightboxPhotoView = LightboxPhotoView;
NS.LightboxView = LightboxView;

}( this.O ) );
<commit_msg>Fix IE lightbox bug.<commit_after>// -------------------------------------------------------------------------- \\
// File: LightboxView.js                                                      \\
// Module: View                                                               \\
// Requires: Core, Foundation, DOM, View.js                                   \\
// Author: Neil Jenkins                                                       \\
// License: © 2010–2012 Opera Software ASA. All rights reserved.              \\
// -------------------------------------------------------------------------- \\

/*global location*/

"use strict";

( function ( NS ) {

var LightboxItemView = NS.Class({

    Extends: NS.View,

    Mixin: NS.AnimatableView,

    lightbox: null,
    index: 0,
    activeIndex: -1,

    frameThickness: 30,

    position: function () {
        return this.get( 'index' ) - this.get( 'activeIndex' );
    }.property( 'activeIndex' ),

    isActive: NS.bind( 'lightbox.isActive', null, NS.Transform.toBoolean ),
    hideControls: true,

    className: function () {
        return 'LightboxItemView' +
            ( this.get( 'hideControls' ) ? ' hideControls' : '' );
    }.property( 'hideControls' ),

    positioning: 'absolute',

    draw: function ( layer ) {
        var Element = NS.Element,
            el = Element.create;

        Element.appendChildren( layer, [
            this.renderContent(),
            this._close = el( 'a.close', { href: location.href }, [
                el( 'span.navLink', [ NS.loc( 'Close' ) ] )
            ])
        ]);
    },

    updateDrawProperties: function () {
        var position = this.get( 'position' ),
            layout =
                !position ?
                    this.get( 'isActive' ) ? 'centreLayout' : 'thumbLayout' :
                position > 0 ?
                    'rightLayout' : 'leftLayout';

        this.set( 'animateLayerDuration', layout === 'thumbLayout' ? 350: 500 )
            .set( 'hideControls', !!position )
            .set( 'layout', this.get( layout ) );
    }.observes( 'isActive', 'position', 'dimensions' ),

    // --- Animation ---

    didAnimate: function () {
        this.increment( 'animating', -1 );
        if ( !this.get( 'position' ) && !this.get( 'isActive' ) ) {
            var lightbox = this.get( 'lightbox' );
            if ( lightbox ) {
                lightbox.removeFromDocument();
            }
        }
    },

    // --- Layout ---

    thumbPosition: {
        top: 0,
        right: 0,
        bottom: 0,
        left: 0
    },

    contentDimensions: {
        width: 300,
        height: 300
    },

    didAppendLayerToDocument: function () {
        // Must flush CSS changes -> force reflow/redraw.
        if ( !this.get( 'position' ) ) {
            this.get( 'layer' ).getBoundingClientRect();
        }
        return LightboxItemView.parent.didAppendLayerToDocument.call( this );
    },

    parentViewDidResize: function () {
        this.computedPropertyDidChange( 'dimensions' );
    },

    dimensions: function () {
        var content = this.get( 'contentDimensions' ),
            width = content.width,
            height = content.height,
            frame = this.get( 'frameThickness' ),

            lightbox = this.get( 'lightbox' ),
            lightboxWidth = lightbox.get( 'pxWidth' ),
            lightboxHeight = lightbox.get( 'pxHeight' ),

            // 75 is the minimum gap to the edge of the screen.
            maxWidth = Math.min( width, lightboxWidth - frame - 75 ),
            maxHeight = Math.min( height, lightboxHeight - frame - 75 ),

            ratio = Math.max( width / maxWidth, height / maxHeight );

        if ( ratio !== 1 ) {
            width = parseInt( width / ratio, 10 );
            height = parseInt( height / ratio, 10 );
        }

        return {
            canvasWidth: lightboxWidth,
            canvasHeight: lightboxHeight,

            width: width,
            height: height
        };
    }.property( 'contentDimensions', 'frameThickness' ),

    thumbLayout: function () {
        var dimensions = this.get( 'dimensions' ),
            width = dimensions.width,
            height = dimensions.height,
            frame = this.get( 'frameThickness' ),
            thumb = this.get( 'thumbPosition' );

        return {
            width: width,
            height: height,
            // IE8 doesn't support bounds.(width|height)
            translateX: Math.floor( thumb.left +
                ( thumb.right - thumb.left - width - frame ) / 2 ),
            translateY: Math.floor( thumb.top +
                ( thumb.bottom - thumb.top - height - frame ) / 2 ),
            scale: ( thumb.right - thumb.left ) / ( width + frame )
        };
    }.property( 'dimensions', 'thumbPosition' ),

    centreLayout: function () {
        var dimensions = this.get( 'dimensions' ),
            width = dimensions.width,
            height = dimensions.height,
            frame = this.get( 'frameThickness' );

        return {
            width: width,
            height: height,
            translateX: ( dimensions.canvasWidth - width - frame ) >> 1,
            translateY: ( dimensions.canvasHeight - height - frame ) >> 1
        };
    }.property( 'dimensions' ),

    leftLayout: function () {
        var dimensions = this.get( 'dimensions' ),
            width = dimensions.width,
            height = dimensions.height,
            frame = this.get( 'frameThickness' );

        return {
            width: dimensions.width,
            height: dimensions.height,
            translateX: -( width + frame ),
            translateY: ( dimensions.canvasHeight - height - frame ) >> 1,
            scale: 0.5
        };
    }.property( 'dimensions' ),

    rightLayout: function () {
        var dimensions = this.get( 'dimensions' ),
            width = dimensions.width,
            height = dimensions.height,
            frame = this.get( 'frameThickness' );

        return {
            width: width,
            height: height,
            translateX: dimensions.canvasWidth,
            translateY: ( dimensions.canvasHeight - height - frame ) >> 1,
            scale: 0.5
        };
    }.property( 'dimensions' ),

    // --- Event handling ---

    close: function () {
        this.get( 'lightbox' ).close();
    },

    onClick: function ( event ) {
        if ( NS.Element.contains( this._close, event.target ) ) {
            event.preventDefault();
            this.close();
        }
        event.stopPropagation();
    }.on( 'click' )
});

var LightboxPhotoView = NS.Class({

    Extends: LightboxItemView,

    className: function () {
        return 'LightboxItemView LightboxPhotoView' +
            ( this.get( 'hideControls' ) ? ' hideControls' : '' );
    }.property( 'hideControls' ),

    didAnimate: function () {
        LightboxPhotoView.parent.didAnimate.call( this );
        if ( this._highResShowing && this.get( 'position' ) ) {
            this.switchToLowRes();
        }
    },

    thumbImage: function () {
        return NS.Element.create( 'img', {
            src: this.get( 'imgThumb' ).src,
            alt: ''
        });
    }.property(),

    getSrcForWidth: function ( width ) {
        var src = this.get( 'imgSrc' );
        return src + ( src.indexOf( '?' ) > -1 ? '&' : '?' ) +
            'width=' + width;
    },

    _highResShowing: false,

    highResLoaded: false,

    highResImage: function () {
        var img = NS.Element.create( 'img', { alt: '' } );
        img.onload = function () {
            this.set( 'highResLoaded', true );
            img = img.onload = null;
        }.invokeInRunLoop().bind( this );
        img.src =
            this.getSrcForWidth( this.get( 'centreLayout' ).width );
        return img;
    }.property(),

    switchToHighRes: function () {
        if ( this.get( 'isRendered' ) &&
                !this.get( 'position' ) &&
                !this._highResShowing ) {
            var img = this.get( 'highResImage' );
            if ( this.get( 'highResLoaded' ) ) {
                this.get( 'layer' ).replaceChild(
                    img, this.get( 'thumbImage' ) );
                this._highResShowing = true;
            }
        }
    }.queue( 'render' ).observes( 'position', 'highResLoaded' ),

    switchToLowRes: function () {
        this.get( 'layer' ).replaceChild(
            this.get( 'thumbImage' ), this.get( 'highResImage' ) );
        this._highResShowing = false;
    }.queue( 'render' ),

    renderContent: function () {
        if ( !this.get( 'position' ) ) {
            this.get( 'highResImage' );
        }
        return this.get( 'thumbImage' );
    },

    // --- Layout ---

    thumbPosition: function () {
        return this.get( 'imgThumb' ).getBoundingClientRect();
    }.property(),

    contentDimensions: function () {
        return {
            width: this.get( 'imgWidth' ),
            height: this.get( 'imgHeight' )
        };
    }.property()
});

var LightboxView = NS.Class({

    Extends: NS.View,

    isActive: false,

    init: function ( parentView, options ) {
        LightboxView.parent.init.call( this, options );
        this._rootView = parentView;
        this._shortcuts = NS.RootViewController.kbShortcuts;
    },

    index: function ( index ) {
        index = index ? index.mod( this.get( 'total' ) ) : 0;
        this.get( 'childViews' ).forEach( function ( view ) {
            view.set( 'activeIndex', index );
        });
        return index;
    }.property(),

    total: 0,

    className: function () {
        return 'LightboxView' +
            ( this.get( 'isActive' ) ? ' active' : '' ) +
            ( this.get( 'total' ) < 2 ? ' hideControls' : '' );
    }.property( 'isActive', 'total' ),

    positioning: 'absolute',
    layout: NS.View.LAYOUT_FILL_PARENT,

    draw: function ( layer ) {
        var Element = NS.Element,
            el = Element.create;

        Element.appendChildren( layer, [
            el( 'div.background' ),
            el( 'a.prev.navLink', { href: location.href }, [
                NS.loc( 'Previous' )
            ]),
            el( 'a.next.navLink', { href: location.href }, [
                NS.loc( 'Next' )
            ])
        ]);
        LightboxView.parent.draw.call( this, layer );
    },

    // add/remove

    add: function ( view, activeIndex ) {
        this.increment( 'total', 1 );

        if ( activeIndex !== undefined ) {
            this.set( 'index', activeIndex );
        }

        view.set( 'lightbox', this )
            .set( 'index', this.get( 'total' ) - 1 )
            .set( 'activeIndex', this.get( 'index' ) );

        this.insertView( view );
    },

    remove: function ( view ) {
        this.removeView( view )
            .increment( 'total', -1 )
            .increment( 'index', 0 );
    },

    // Open/close

    open: function ( lightboxViews, startIndex ) {
        // Get list of item views.
        if ( !( lightboxViews instanceof Array ) ) {
            lightboxViews = [ lightboxViews ];
        }

        // Set index/total
        this.set( 'total', lightboxViews.length )
            .set( 'index', startIndex || ( startIndex = 0 ) );

        var l = lightboxViews.length,
            view;
        while ( l-- ) {
            view = lightboxViews[l];
            view.set( 'lightbox', this )
                .set( 'index', l )
                .set( 'activeIndex', startIndex );

            this.insertView( view );
        }

        // Disable keyboard shortcuts
        this._kbEnabled = this._shortcuts.get( 'isEnabled' );
        this._shortcuts.set( 'isEnabled', false );

        // Insert view
        this._rootView.insertView( this );

        // Capture events
        NS.RootViewController.queueResponder( this );

        // Now, fade in gradient background and views.
        this.set( 'isActive', true );
    },

    close: function () {
        // Reenable keyboard shortcuts
        this._shortcuts.set( 'isEnabled', this._kbEnabled );

        // Stop capturing events
        NS.RootViewController.removeResponder( this );

        // Fade out gradient background and views.
        // Item view will call removeFromDocument after animation.
        this.set( 'isActive', false );
    },

    removeFromDocument: function () {
        this._rootView.removeView( this );

        var children = this.get( 'childViews' ),
            l = children.length,
            view;

        while ( l-- ) {
            view = children[l];
            this.removeView( view );
            view.set( 'lightbox', null );
        }
    }.queue( 'render' ),

    // Dimensions

    pxWidth: function () {
        return this._rootView.get( 'pxWidth' );
    }.property().nocache(),

    pxHeight: function () {
        return this._rootView.get( 'pxHeight' );
    }.property().nocache(),

    // Actions

    keyboardShortcuts: function ( event ) {
        switch ( NS.DOMEvent.lookupKey( event ) ) {
            case 'esc':
                this.close();
                break;
            case 'right':
                event.preventDefault();
                this.increment( 'index', 1 );
                break;
            case 'left':
                event.preventDefault();
                this.increment( 'index', -1 );
                break;
        }
        event.stopPropagation();
    }.on( 'keydown' ),

    onClick: function ( event ) {
        if ( event.button || event.metaKey || event.ctrlKey ) { return; }
        var action = event.target.className;
        if ( /prev/.test( action ) ) {
            this.increment( 'index', -1 );
        } else if ( /next/.test( action ) ) {
            this.increment( 'index', 1 );
        } else {
            this.close();
        }
        event.preventDefault();
        event.stopPropagation();
    }.on( 'click' )
});

NS.LightboxItemView = LightboxItemView;
NS.LightboxPhotoView = LightboxPhotoView;
NS.LightboxView = LightboxView;

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


<commit_before>window.cls || (window.cls = {});

cls.PoTestView = function(id, name, container_class)
{
  this._podata = null;
  this._file = null;

  this.createView = function(container)
  {
    container.clearAndRender(templates.po_main(this._podata));
  }

  this._on_selected_po_bound = function(evt, target)
  {
    this._file = target.files[0];
    this._load_current_file();
  }.bind(this);

  this._load_current_file = function()
  {
    var reader = new FileReader();
    reader.onload = this._on_loaded_po_bound;
    reader.onerror = function() { opera.postError("arrar!") } // fixme
    reader.readAsText(this._file);
  }

  this._on_loaded_po_bound = function(evt)
  {
    data = potools.parse(evt.target.result, "dragonfly");
    this._podata = data;
    this._update_uistrings();
  }.bind(this);

  this._on_clicked_reload_po_bound = function()
  {
    this._load_current_file();
  }.bind(this);

  this._on_clicked_select_po_bound = function()
  {
    this._podata = null;
    this._file = null;
    this.update();
  }.bind(this);

  this._update_uistrings = function()
  {
    var unhandled = [];
    for (var n=0, entry; entry=this._podata[n]; n++)
    {
      if (entry.msgctxt in window.ui_strings)
      {
        window.ui_strings[entry.msgctxt] = entry.msgstr;
      }
      else
      {
        unhandled.push(entry);
      }
    }
    client.setup();
  }

  var eh = window.eventHandlers;
  eh.change["po-file-selected"] = this._on_selected_po_bound;
  eh.click["reload-po-data"] = this._on_clicked_reload_po_bound;
  eh.click["select-other-po"] = this._on_clicked_select_po_bound;

  this.init(id, name, container_class);
}
cls.PoTestView.prototype = ViewBase;

window.templates = window.templates || {};

window.templates.po_main = function(podata)
{
  if (podata)
  {
    var bad_markers = potools.malformed_interpolation_markers(podata);
    var missing_markers = potools.missing_interpolation_markers(podata);
    var errortpls = [];

    if (bad_markers.length) {
      errortpls.push(
        ["p", "Some translated strings contain bad interpolation markers! Missing s at the end: " +
              bad_markers.map(function(e) { return e.msgctxt }).join(", ")]
      )
    }

    if (missing_markers.length) {
      errortpls.push(
        ["p", "Some translated strings interpolation markers do not match the English version: " +
              missing_markers.map(function(e) { return e.msgctxt }).join(", ")]
      )
    }

   return ["div", [
            ["p", "Loaded a PO file with " + podata.length + " entries."],
            ["button", "Reload PO file", "handler", "reload-po-data"],
            ["button", "Load another PO file", "handler", "select-other-po"],
            errortpls,
          "class", "padding"]
          ];
  }
  else
  {
    return [
      "div", [
        ["label", "Select a po file to load: "],
        ["input", "Paste the contents of a po file", "type", "file", "handler", "po-file-selected"],
      "class", "padding"]
    ]
  }
}<commit_msg>Fixed bad posterror in po test code<commit_after>window.cls || (window.cls = {});

cls.PoTestView = function(id, name, container_class)
{
  this._podata = null;
  this._file = null;

  this.createView = function(container)
  {
    container.clearAndRender(templates.po_main(this._podata));
  }

  this._on_selected_po_bound = function(evt, target)
  {
    this._file = target.files[0];
    this._load_current_file();
  }.bind(this);

  this._load_current_file = function()
  {
    var reader = new FileReader();
    reader.onload = this._on_loaded_po_bound;
    reader.onerror = function() { opera.postError("Error reloading current file") };
    reader.readAsText(this._file);
  }

  this._on_loaded_po_bound = function(evt)
  {
    data = potools.parse(evt.target.result, "dragonfly");
    this._podata = data;
    this._update_uistrings();
  }.bind(this);

  this._on_clicked_reload_po_bound = function()
  {
    this._load_current_file();
  }.bind(this);

  this._on_clicked_select_po_bound = function()
  {
    this._podata = null;
    this._file = null;
    this.update();
  }.bind(this);

  this._update_uistrings = function()
  {
    var unhandled = [];
    for (var n=0, entry; entry=this._podata[n]; n++)
    {
      if (entry.msgctxt in window.ui_strings)
      {
        window.ui_strings[entry.msgctxt] = entry.msgstr;
      }
      else
      {
        unhandled.push(entry);
      }
    }
    client.setup();
  }

  var eh = window.eventHandlers;
  eh.change["po-file-selected"] = this._on_selected_po_bound;
  eh.click["reload-po-data"] = this._on_clicked_reload_po_bound;
  eh.click["select-other-po"] = this._on_clicked_select_po_bound;

  this.init(id, name, container_class);
}
cls.PoTestView.prototype = ViewBase;

window.templates = window.templates || {};

window.templates.po_main = function(podata)
{
  if (podata)
  {
    var bad_markers = potools.malformed_interpolation_markers(podata);
    var missing_markers = potools.missing_interpolation_markers(podata);
    var errortpls = [];

    if (bad_markers.length) {
      errortpls.push(
        ["p", "Some translated strings contain bad interpolation markers! Missing s at the end: " +
              bad_markers.map(function(e) { return e.msgctxt }).join(", ")]
      )
    }

    if (missing_markers.length) {
      errortpls.push(
        ["p", "Some translated strings interpolation markers do not match the English version: " +
              missing_markers.map(function(e) { return e.msgctxt }).join(", ")]
      )
    }

   return ["div", [
            ["p", "Loaded a PO file with " + podata.length + " entries."],
            ["button", "Reload PO file", "handler", "reload-po-data"],
            ["button", "Load another PO file", "handler", "select-other-po"],
            errortpls,
          "class", "padding"]
          ];
  }
  else
  {
    return [
      "div", [
        ["label", "Select a po file to load: "],
        ["input", "Paste the contents of a po file", "type", "file", "handler", "po-file-selected"],
      "class", "padding"]
    ]
  }
}<|endoftext|>