<commit_before>define([
    'streamhub-gallery/horizontal-list-view',
    'text!streamhub-gallery/css/gallery-view.css',
    'hgn!streamhub-gallery/templates/gallery-view',
    'hgn!streamhub-gallery/css/theme.css',
    'streamhub-sdk/util'
], function (HorizontalListView, GalleryViewCss, GalleryViewTemplate, ThemeCssTemplate, util) {

    var STYLE_EL,
        GALLERY_THEME_STYLE_EL = $('');

    var GALLERY_CSS = {
        contentBefore: {
            transforms: {
                translateX: '-9999px',
                scale: 0.45
            }
        },
        contentAfter: {
            transforms: {
                translateX: '9999px',
                scale: 0.45
            }
        }
    };
    GALLERY_CSS.contentBefore1 = { opacity: 0.7 };
    GALLERY_CSS.contentBefore2 = { opacity: 0.3 };
    GALLERY_CSS.contentBefore3 = { opacity: 0.1 };
    GALLERY_CSS.contentAfter1 = { opacity: 0.7 };
    GALLERY_CSS.contentAfter2 = { opacity: 0.3 };
    GALLERY_CSS.contentAfter3 = { opacity: 0.1 };

    var GalleryView = function (opts) {
        opts = opts || {};
        opts.modal = opts.modal || true;
        opts.aspectRatio = opts.aspectRatio || 16/9;

        this._activeContentView = null;
        this._newContentCount = 0;
        HorizontalListView.call(this, opts);

        this._id = this.galleryListViewClassName + '-' + new Date().getTime();

        if (!STYLE_EL) {
            STYLE_EL = $('').text(GalleryViewCss).prependTo('head');
        }
    };
    util.inherits(GalleryView, HorizontalListView);

    GalleryView.prototype.template = GalleryViewTemplate;
    GalleryView.prototype.galleryListViewClassName = 'streamhub-gallery-view';

    GalleryView.prototype.setElement = function (el) {
        var galleryViewEl = $(this.template());
        this.el = galleryViewEl.filter('.'+this.galleryListViewClassName);
        HorizontalListView.prototype.setElement.call(this, this.el);
        galleryViewEl.appendTo(el);

        var self = this;
        this.$el.on('focusContent.hub', function (e) {
            var contentEl = $(e.target).hasClass('content') ? e.target : $(e.target).closest('article.content')[0];
            if ($(contentEl).parent().hasClass('content-before') || $(contentEl).parent().hasClass('content-after')) {
                e.stopImmediatePropagation();
            }
        });

        this.$el.on('click', '.content-before, .content-after', function (e) {
            e.preventDefault();
            e.stopPropagation();

            var targetContentView;
            for (var i=0; i < self.contentViews.length; i++) {
                var contentEl = $(e.target).hasClass('content') ? e.target : $(e.target).closest('article.content')[0];
                if (self.contentViews[i].el === contentEl) {
                    targetContentView = self.contentViews[i];
                    break;
                }
            }
            self.jump(targetContentView);
        });

        $('.streamhub-gallery-view-prev > .streamhub-gallery-view-page-button').on('click', function (e) {
            e.preventDefault();
            self.prev();
        });

        $('.streamhub-gallery-view-next > .streamhub-gallery-view-page-button').on('click', function (e) {
            e.preventDefault();
            self.next();
        });

        $('.streamhub-gallery-view-notification').on('click', function (e) {
            e.preventDefault();
            // Jump to head when the notification is clicked
            self.jump(self.contentViews[0]);
            self._hideNewNotification();
        });

        this.$el.on('imageLoaded.hub', function (e) {
            self._adjustContentSize();
        });
    };

    GalleryView.prototype.add = function (content) {
        var contentView = HorizontalListView.prototype.add.call(this, content);
        // If there is new content and we're not focused at the head, show notification
        if (this.contentViews.indexOf(this._activeContentView) == 0) {
            return;
        }
        this._newContentCount++;
        this._showNewNotification();
        return contentView;
    };

    GalleryView.prototype._showNewNotification = function () {
        var notificationEl = $('.streamhub-gallery-view-notification');
        notificationEl.find('.streamhub-gallery-view-notification-count').html(this._newContentCount);
        notificationEl.fadeIn();
    };

    GalleryView.prototype._hideNewNotification = function () {
        var notificationEl = $('.streamhub-gallery-view-notification');
        this._newContentCount = 0;
        notificationEl.fadeOut();
    };

    GalleryView.prototype._insert = function (contentView) {
        var self = this,
            newContentViewIndex,
            $previousEl;

        newContentViewIndex = this.contentViews.indexOf(contentView);

        var $containerEl = $('

');
        contentView.$el.wrap($containerEl);
        var $wrappedEl = contentView.$el.parent();

        if (newContentViewIndex === 0) {
            // Beginning!
            $wrappedEl.prependTo(this.el);
        } else {
            // Find it's previous contentView and insert new contentView after
            $previousEl = this.contentViews[newContentViewIndex - 1].$el;
            $wrappedEl.insertAfter($previousEl.parent('.'+this.contentContainerClassName));
        }

        this.focus();
    };

    GalleryView.prototype.jump = function (contentView) {
        var contentViewIndex = this.contentViews.indexOf(contentView);
        if (contentViewIndex == 0) {
            newContentCount = 0;
        }
        this.$el.removeClass('animate');
        var originalActiveContentView = this._activeContentView;
        // Apply transforms exclusive of translations to calculate spacing
        var newTransforms = $.extend(true, {}, this.focus({
            translate: false,
            contentView: contentView
        }));
        // Revert to original state of spacing
        this.focus({
            contentView: originalActiveContentView
        });
        // Apply calculated transforms to original state
        var self = this;
        setTimeout(function () {
            self.$el.addClass('animate');
            self.focus({
                translate: newTransforms,
                contentView: contentView
            });
        },1);
    };

    GalleryView.prototype.next = function () {
        var activeIndex = this.contentViews.indexOf(this._activeContentView);
        var targetContentView = this.contentViews[Math.min(activeIndex+1, this.contentViews.length-1)];
        this.jump(targetContentView);
    };

    GalleryView.prototype.prev = function () {
        var activeIndex = this.contentViews.indexOf(this._activeContentView);
        var targetContentView = this.contentViews[activeIndex-1 || 0];
        this.jump(targetContentView);
    };

    GalleryView.prototype.focus = function (opts) {
        if (! this._activeContentView) {
            this._activeContentView = this.contentViews[0];
        }

        opts = opts || {};

        var contentContainerEls = this.$el.find('.content-container');
        contentContainerEls.removeClass('content-active')
            .removeClass('content-before-3')
            .removeClass('content-before-2')
            .removeClass('content-before-1')
            .removeClass('content-after-3')
            .removeClass('content-after-2')
            .removeClass('content-after-1')
            .removeClass('content-before')
            .removeClass('content-after')
            .removeAttr('style');

        this._activeContentView = opts.contentView ? opts.contentView : this._activeContentView;
        var activeIndex = this.contentViews.indexOf(this._activeContentView);

        var targetContentEl = this.contentViews[activeIndex].$el;
        var targetContainerEl = targetContentEl.parent();
        targetContainerEl.addClass('content-active');
        targetContainerEl.prevAll().addClass('content-before');
        targetContainerEl.nextAll().addClass('content-after');
        var before1 = targetContainerEl.prev().addClass('content-before-1');
        var before2 = before1.prev().addClass('content-before-2');
        var before3 = before2.prev().addClass('content-before-3');
        var after1 = targetContainerEl.next().addClass('content-after-1');
        var after2 = after1.next().addClass('content-after-2');
        var after3 = after2.next().addClass('content-after-3');

        return this._adjustContentSize(opts);
    };

    GalleryView.prototype._getContentSize = function () {
        var containerWidth = this.$el.width();
        var contentHeight = this.$el.height();
        var contentWidth = Math.min(contentHeight * this._aspectRatio, containerWidth);

        if (contentWidth == containerWidth) {
            contentHeight = contentHeight * 0.8;
            contentWidth = contentHeight * this._aspectRatio;
        }

        return { width: contentWidth, height: contentHeight };
    };

    GalleryView.prototype._adjustContentSize = function (opts) {
        var styleEl = $('style.'+this._id);
        if (styleEl) {
            styleEl.remove();
        }

        styleEl = $('');
        var styles = '';
        var contentSize = this._getContentSize();
        styles = '.'+this.horizontalListViewClassName + ' .'+this.contentContainerClassName + ' { width: ' + contentSize.width + 'px; height: ' + contentSize.height + 'px; margin-left: '+ contentSize.width/-2 + 'px; margin-top: '+ contentSize.height/-2+ 'px; }';
        styleEl.html(styles);
        $('head').append(styleEl);

        // Make content with tiled attachments square except when there's a
        // video attachment
        var contentWithImageEls = this.$el.find('.content-with-image');
        for (var i=0; i < contentWithImageEls.length; i++) {
            var contentEl = contentWithImageEls.eq(i).closest('.content-container');
            if (contentEl.find('.content-attachment-video').length) {
                contentEl.find('.content, .content-attachment').css({
                    'padding-bottom': 1/this._aspectRatio * 100 + '%',
                });
            } else {
                contentEl.css({
                    'width': contentSize.height + 'px',
                    'height': contentSize.height + 'px',
                    'margin-left': contentSize.height/-2 + 'px',
                    'margin-top': contentSize.height/-2 + 'px'
                });
            }
        }

        return this._adjustContentSpacing(opts);
    };

    GalleryView.prototype._adjustContentSpacing = function (opts) {
        return this._slideshowSpacing(opts);
    };

    GalleryView.prototype._slideshowSpacing = function (opts) {
        opts = opts || {};
        var visibleAdjacentContent = 3;

        if (opts.translate) {
            GALLERY_CSS = opts.translate;
            this._updateStyleEl(opts.translate);
            return;
        }

        var adjacentContentEls = this.$el.find('.content-before, .content-after, .content-active');
        if (!adjacentContentEls.length) {
            return;
        }

        var beforeTranslateX = 0;
        var afterTranslateX = 0;
        for (var i=0; i < visibleAdjacentContent; i++) {
            var adjacentIndex = i+1;

            // Before
            var contentBefore = adjacentContentEls.filter('.content-before-'+adjacentIndex);
            var contentBeforeWidth;
            if (contentBefore.length) {
                GALLERY_CSS['contentBefore'+adjacentIndex].transforms = $.extend({}, GALLERY_CSS.contentBefore.transforms);
                contentBeforeWidth = contentBefore[0].getBoundingClientRect().width;
                var previousEl = contentBefore.next();
                var previousWidth = previousEl[0].getBoundingClientRect().width;
                beforeTranslateX = beforeTranslateX - previousWidth - (contentBeforeWidth - previousWidth)/2;
                GALLERY_CSS['contentBefore'+adjacentIndex].transforms.translateX = beforeTranslateX+'px';
            }

            // After
            var contentAfter = adjacentContentEls.filter('.content-after-'+adjacentIndex);
            var contentAfterWidth;
            if (contentAfter.length) {
                GALLERY_CSS['contentAfter'+adjacentIndex].transforms = $.extend({}, GALLERY_CSS.contentAfter.transforms);
                contentAfterWidth = contentAfter[0].getBoundingClientRect().width;
                var previousEl = contentAfter.prev();
                var previousWidth = previousEl[0].getBoundingClientRect().width;
                afterTranslateX = afterTranslateX + previousWidth + (contentAfterWidth - previousWidth)/2
                GALLERY_CSS['contentAfter'+adjacentIndex].transforms.translateX = afterTranslateX+'px';
            }
        }

        this._updateStyleEl(opts.translate);

        return GALLERY_CSS;
    };

    GalleryView.prototype._updateStyleEl = function (translate) {
        translate = translate === undefined ? true : translate;
        for (var style in GALLERY_CSS) {
            var transform = '';
            for (var t in GALLERY_CSS[style].transforms) {
                if (translate || style == 'contentBefore' || style == 'contentAfter' || (!translate && t.indexOf('translate') == -1)) {
                    transform = transform + t + '(' + GALLERY_CSS[style].transforms[t]  + ') ';
                }
            }
            GALLERY_CSS[style].transform = transform;
        }
        var styleInnerHtml = ThemeCssTemplate(GALLERY_CSS);
        var matches = styleInnerHtml.match(new RegExp("(\A|\})\s*(?![^ ~>|]*\.*\{)", 'g'));
        for (var i=0; i < matches.length; i++) {
            var idx = styleInnerHtml.indexOf(matches[i]);
            styleInnerHtml = styleInnerHtml.slice(0, idx) +
                this._id + styleInnerHtml.slice(idx);
        }

        GALLERY_THEME_STYLE_EL.remove();
        GALLERY_THEME_STYLE_EL = $('').text(styleInnerHtml).appendTo('head');
    };

    return GalleryView;
});
<commit_msg>Add scaling of content items when the active slide is too wide<commit_after>define([
    'streamhub-gallery/horizontal-list-view',
    'text!streamhub-gallery/css/gallery-view.css',
    'hgn!streamhub-gallery/templates/gallery-view',
    'hgn!streamhub-gallery/css/theme.css',
    'streamhub-sdk/util'
], function (HorizontalListView, GalleryViewCss, GalleryViewTemplate, ThemeCssTemplate, util) {

    var STYLE_EL,
        GALLERY_THEME_STYLE_EL = $('');

    var GALLERY_CSS = {
        contentBefore: {
            transforms: {
                translateX: '-9999px',
                scale: 0.45
            }
        },
        contentAfter: {
            transforms: {
                translateX: '9999px',
                scale: 0.45
            }
        }
    };
    GALLERY_CSS.contentBefore1 = { opacity: 0.7 };
    GALLERY_CSS.contentBefore2 = { opacity: 0.3 };
    GALLERY_CSS.contentBefore3 = { opacity: 0.1 };
    GALLERY_CSS.contentAfter1 = { opacity: 0.7 };
    GALLERY_CSS.contentAfter2 = { opacity: 0.3 };
    GALLERY_CSS.contentAfter3 = { opacity: 0.1 };

    var GalleryView = function (opts) {
        opts = opts || {};
        opts.modal = opts.modal || true;
        opts.aspectRatio = opts.aspectRatio || 16/9;

        this._activeContentView = null;
        this._newContentCount = 0;
        HorizontalListView.call(this, opts);

        this._id = this.galleryListViewClassName + '-' + new Date().getTime();

        if (!STYLE_EL) {
            STYLE_EL = $('').text(GalleryViewCss).prependTo('head');
        }
    };
    util.inherits(GalleryView, HorizontalListView);

    GalleryView.prototype.template = GalleryViewTemplate;
    GalleryView.prototype.galleryListViewClassName = 'streamhub-gallery-view';

    GalleryView.prototype.setElement = function (el) {
        var galleryViewEl = $(this.template());
        this.el = galleryViewEl.filter('.'+this.galleryListViewClassName);
        HorizontalListView.prototype.setElement.call(this, this.el);
        galleryViewEl.appendTo(el);

        var self = this;
        this.$el.on('focusContent.hub', function (e) {
            var contentEl = $(e.target).hasClass('content') ? e.target : $(e.target).closest('article.content')[0];
            if ($(contentEl).parent().hasClass('content-before') || $(contentEl).parent().hasClass('content-after')) {
                e.stopImmediatePropagation();
            }
        });

        this.$el.on('click', '.content-before, .content-after', function (e) {
            e.preventDefault();
            e.stopPropagation();

            var targetContentView;
            for (var i=0; i < self.contentViews.length; i++) {
                var contentEl = $(e.target).hasClass('content') ? e.target : $(e.target).closest('article.content')[0];
                if (self.contentViews[i].el === contentEl) {
                    targetContentView = self.contentViews[i];
                    break;
                }
            }
            self.jump(targetContentView);
        });

        $('.streamhub-gallery-view-prev > .streamhub-gallery-view-page-button').on('click', function (e) {
            e.preventDefault();
            self.prev();
        });

        $('.streamhub-gallery-view-next > .streamhub-gallery-view-page-button').on('click', function (e) {
            e.preventDefault();
            self.next();
        });

        $('.streamhub-gallery-view-notification').on('click', function (e) {
            e.preventDefault();
            // Jump to head when the notification is clicked
            self.jump(self.contentViews[0]);
            self._hideNewNotification();
        });

        this.$el.on('imageLoaded.hub', function (e) {
            self._adjustContentSize();
        });
    };

    GalleryView.prototype.add = function (content) {
        var contentView = HorizontalListView.prototype.add.call(this, content);
        // If there is new content and we're not focused at the head, show notification
        if (this.contentViews.indexOf(this._activeContentView) == 0) {
            return;
        }
        this._newContentCount++;
        this._showNewNotification();
        return contentView;
    };

    GalleryView.prototype._showNewNotification = function () {
        var notificationEl = $('.streamhub-gallery-view-notification');
        notificationEl.find('.streamhub-gallery-view-notification-count').html(this._newContentCount);
        notificationEl.fadeIn();
    };

    GalleryView.prototype._hideNewNotification = function () {
        var notificationEl = $('.streamhub-gallery-view-notification');
        this._newContentCount = 0;
        notificationEl.fadeOut();
    };

    GalleryView.prototype._insert = function (contentView) {
        var self = this,
            newContentViewIndex,
            $previousEl;

        newContentViewIndex = this.contentViews.indexOf(contentView);

        var $containerEl = $('

');
        contentView.$el.wrap($containerEl);
        var $wrappedEl = contentView.$el.parent();

        if (newContentViewIndex === 0) {
            // Beginning!
            $wrappedEl.prependTo(this.el);
        } else {
            // Find it's previous contentView and insert new contentView after
            $previousEl = this.contentViews[newContentViewIndex - 1].$el;
            $wrappedEl.insertAfter($previousEl.parent('.'+this.contentContainerClassName));
        }

        this.focus();
    };

    GalleryView.prototype.jump = function (contentView) {
        var contentViewIndex = this.contentViews.indexOf(contentView);
        if (contentViewIndex == 0) {
            newContentCount = 0;
        }
        this.$el.removeClass('animate');
        var originalActiveContentView = this._activeContentView;
        // Apply transforms exclusive of translations to calculate spacing
        var newTransforms = $.extend(true, {}, this.focus({
            translate: false,
            contentView: contentView
        }));
        // Revert to original state of spacing
        this.focus({
            contentView: originalActiveContentView
        });
        // Apply calculated transforms to original state
        var self = this;
        setTimeout(function () {
            self.$el.addClass('animate');
            self.focus({
                translate: newTransforms,
                contentView: contentView
            });
        },1);
    };

    GalleryView.prototype.next = function () {
        var activeIndex = this.contentViews.indexOf(this._activeContentView);
        var targetContentView = this.contentViews[Math.min(activeIndex+1, this.contentViews.length-1)];
        this.jump(targetContentView);
    };

    GalleryView.prototype.prev = function () {
        var activeIndex = this.contentViews.indexOf(this._activeContentView);
        var targetContentView = this.contentViews[activeIndex-1 || 0];
        this.jump(targetContentView);
    };

    GalleryView.prototype.focus = function (opts) {
        if (! this._activeContentView) {
            this._activeContentView = this.contentViews[0];
        }

        opts = opts || {};

        var contentContainerEls = this.$el.find('.content-container');
        contentContainerEls.removeClass('content-active')
            .removeClass('content-before-3')
            .removeClass('content-before-2')
            .removeClass('content-before-1')
            .removeClass('content-after-3')
            .removeClass('content-after-2')
            .removeClass('content-after-1')
            .removeClass('content-before')
            .removeClass('content-after')
            .removeAttr('style');

        this._activeContentView = opts.contentView ? opts.contentView : this._activeContentView;
        var activeIndex = this.contentViews.indexOf(this._activeContentView);

        var targetContentEl = this.contentViews[activeIndex].$el;
        var targetContainerEl = targetContentEl.parent();
        targetContainerEl.addClass('content-active');
        targetContainerEl.prevAll().addClass('content-before');
        targetContainerEl.nextAll().addClass('content-after');
        var before1 = targetContainerEl.prev().addClass('content-before-1');
        var before2 = before1.prev().addClass('content-before-2');
        var before3 = before2.prev().addClass('content-before-3');
        var after1 = targetContainerEl.next().addClass('content-after-1');
        var after2 = after1.next().addClass('content-after-2');
        var after3 = after2.next().addClass('content-after-3');

        return this._adjustContentSize(opts);
    };

    GalleryView.prototype._getContentSize = function () {
        var containerWidth = this.$el.width();
        var contentHeight = this.$el.height();
        var contentWidth = Math.min(contentHeight * this._aspectRatio, containerWidth);

        if (contentWidth/containerWidth <= 0.9) {
            contentHeight = contentHeight * 0.95;
            contentWidth = contentHeight * this._aspectRatio;
        } else if (contentWidth/containerWidth <= 0.95) {
            contentHeight = contentHeight * 0.8;
            contentWidth = contentHeight * this._aspectRatio;
        } else {
            contentHeight = contentHeight * 0.5;
            contentWidth = contentHeight * this._aspectRatio;
        }

        return { width: contentWidth, height: contentHeight };
    };

    GalleryView.prototype._adjustContentSize = function (opts) {
        var styleEl = $('style.'+this._id);
        if (styleEl) {
            styleEl.remove();
        }

        styleEl = $('');
        var styles = '';
        var contentSize = this._getContentSize();
        styles = '.'+this.horizontalListViewClassName + ' .'+this.contentContainerClassName + ' { width: ' + contentSize.width + 'px; height: ' + contentSize.height + 'px; margin-left: '+ contentSize.width/-2 + 'px; margin-top: '+ contentSize.height/-2+ 'px; }';
        styleEl.html(styles);
        $('head').append(styleEl);

        // Make content with tiled attachments square except when there's a
        // video attachment
        var contentWithImageEls = this.$el.find('.content-with-image');
        for (var i=0; i < contentWithImageEls.length; i++) {
            var contentEl = contentWithImageEls.eq(i).closest('.content-container');
            if (contentEl.find('.content-attachment-video').length) {
                contentEl.find('.content, .content-attachment').css({
                    'padding-bottom': 1/this._aspectRatio * 100 + '%',
                });
            } else {
                contentEl.css({
                    'width': contentSize.height + 'px',
                    'height': contentSize.height + 'px',
                    'margin-left': contentSize.height/-2 + 'px',
                    'margin-top': contentSize.height/-2 + 'px'
                });
            }
        }

        return this._adjustContentSpacing(opts);
    };

    GalleryView.prototype._adjustContentSpacing = function (opts) {
        return this._slideshowSpacing(opts);
    };

    GalleryView.prototype._slideshowSpacing = function (opts) {
        opts = opts || {};
        var visibleAdjacentContent = 3;

        if (opts.translate) {
            GALLERY_CSS = opts.translate;
            this._updateStyleEl(opts.translate);
            return;
        }

        var adjacentContentEls = this.$el.find('.content-before, .content-after, .content-active');
        if (!adjacentContentEls.length) {
            return;
        }

        var beforeTranslateX = 0;
        var afterTranslateX = 0;
        for (var i=0; i < visibleAdjacentContent; i++) {
            var adjacentIndex = i+1;

            // Before
            var contentBefore = adjacentContentEls.filter('.content-before-'+adjacentIndex);
            var contentBeforeWidth;
            if (contentBefore.length) {
                GALLERY_CSS['contentBefore'+adjacentIndex].transforms = $.extend({}, GALLERY_CSS.contentBefore.transforms);
                contentBeforeWidth = contentBefore[0].getBoundingClientRect().width;
                var previousEl = contentBefore.next();
                var previousWidth = previousEl[0].getBoundingClientRect().width;
                beforeTranslateX = beforeTranslateX - previousWidth - (contentBeforeWidth - previousWidth)/2;
                GALLERY_CSS['contentBefore'+adjacentIndex].transforms.translateX = beforeTranslateX+'px';
            }

            // After
            var contentAfter = adjacentContentEls.filter('.content-after-'+adjacentIndex);
            var contentAfterWidth;
            if (contentAfter.length) {
                GALLERY_CSS['contentAfter'+adjacentIndex].transforms = $.extend({}, GALLERY_CSS.contentAfter.transforms);
                contentAfterWidth = contentAfter[0].getBoundingClientRect().width;
                var previousEl = contentAfter.prev();
                var previousWidth = previousEl[0].getBoundingClientRect().width;
                afterTranslateX = afterTranslateX + previousWidth + (contentAfterWidth - previousWidth)/2
                GALLERY_CSS['contentAfter'+adjacentIndex].transforms.translateX = afterTranslateX+'px';
            }
        }

        this._updateStyleEl(opts.translate);

        return GALLERY_CSS;
    };

    GalleryView.prototype._updateStyleEl = function (translate) {
        translate = translate === undefined ? true : translate;
        for (var style in GALLERY_CSS) {
            var transform = '';
            for (var t in GALLERY_CSS[style].transforms) {
                if (translate || style == 'contentBefore' || style == 'contentAfter' || (!translate && t.indexOf('translate') == -1)) {
                    transform = transform + t + '(' + GALLERY_CSS[style].transforms[t]  + ') ';
                }
            }
            GALLERY_CSS[style].transform = transform;
        }
        var styleInnerHtml = ThemeCssTemplate(GALLERY_CSS);
        var matches = styleInnerHtml.match(new RegExp("(\A|\})\s*(?![^ ~>|]*\.*\{)", 'g'));
        for (var i=0; i < matches.length; i++) {
            var idx = styleInnerHtml.indexOf(matches[i]);
            styleInnerHtml = styleInnerHtml.slice(0, idx) +
                this._id + styleInnerHtml.slice(idx);
        }

        GALLERY_THEME_STYLE_EL.remove();
        GALLERY_THEME_STYLE_EL = $('').text(styleInnerHtml).appendTo('head');
    };

    return GalleryView;
});
<|endoftext|>


<commit_before>/**
 * blog.js - the core of silentor
 * author: Jayin Ton
 * version: 1.5.4
 * License:Apache 2.0
 */
(function($) {
    'use strict';
    var app_name = '';
    var blog_base = '';
    var img_root = 'img';
    var markdown_root = 'p';
    //当前请求的markdown文件
    var cur_md_path = '';
    /*是否是http:// 如果是，那么这是资源文件,如果否，说明这是要处理的a标签*/
    function isAbsolute(url) {
            return url.indexOf('//') !== -1;
        }
        /*获得相对目录*/
    function getPageBase(url) {
            return url.slice(0, url.lastIndexOf('/') + 1);
        }
        /*判断加载的路径是否是markdown文件*/
    function isMarkdownFile(url) {
            return url.toLowerCase().indexOf('.md') !== -1 || url.toLowerCase().indexOf('.markdown') !== -1;
        }
        //获得markdown的文件名,用作标题
    function getMarkdownTitle(file_path) {
            if (!isMarkdownFile(file_path)) {
                return app_name;
            } else {
                var real_file_name = file_path;
                if (hasFolder(file_path)) {
                    real_file_name = file_path.slice(file_path.lastIndexOf('/') + 1, file_path.length);
                }
                return real_file_name.split('.')[0];
            }
        }
    //Event Hook
    function hook(hook_name, data){
        $(window).trigger(hook_name, data);
    }
        // resolve 路径
    function resolvePath(from, to) {
            if (from[from.length - 1] == '/') {
                from = from.substring(0, from.length - 1);
            }
            var froms = from.split('/');
            var tos = to.split('/');
            if (tos[0] == '.') {
                tos.splice(0, 1);
            } else if (tos[0] == '..') {
                froms.splice(froms.length - 1, 1);
                tos.splice(0, 1);
            } else if (tos[0].indexOf('http') != -1 || tos[0].indexOf('https') != -1) {
                return to;
            } else {
                return froms.join('/') + "/" + tos.join('/');
            }
            return resolvePath(froms.join('/'), tos.join('/'));
        }
        /**
         * @param selector 选择器
         * @param  file_path 文件路径
         * @param  isSidebar 是否是左边导航栏
         * @param  baseUrl 基准url
         */
    function load(selector, file_path, isSidebar, baseUrl) {
        var baseUrl = baseUrl || blog_base;
        var isSidebar = isSidebar || false;

        var p_url = baseUrl + file_path;

        $.ajax({
            type: 'GET',
            url: p_url,
            success: function(data){
                marked.setOptions({
                    highlight: function(code) {
                        return hljs.highlightAuto(code).value;
                    }
                });
                var _selector = $(selector);
                _selector.html(marked(data));

                //处理所有href
                _selector.find('[href]').each(function() {
                    var $element = $(this);
                    var url = $element.attr('href');

                    if (isAbsolute(url)) {
                        $element.attr('target', '_blank');
                    }

                    // sidebar
                    if (isSidebar && isMarkdownFile(url)) {
                        $element.attr('href', '?' + url);

                    }

                    //main page
                    if (!isAbsolute(url) && !isSidebar && isMarkdownFile(url)) {
                        var new_url = getPageBase(cur_md_path);
                        //上一级目录
                        if (url.indexOf('../') == 0) {
                            //处理相对路径
                            new_url = resolvePath(getPageBase(cur_md_path), url);
                        } else if (url.indexOf('__P__') == 0) {
                            //文章根目录`p/`下
                            new_url = url.replace('__P__/', '');
                        } else {
                            //当前目录
                            new_url = new_url + url.replace('./', '');
                        }
                        $element.attr('href', '?' + new_url);
                    }
                });
                //main-page
                if (!isSidebar) {
                    //change title
                    var mainTitle = $('#main-page').find('h1, h2, h3, h4, h5, h6').first().text();
                    $('title').text(mainTitle);

                    //图片位置
                    $.each(_selector.find('img'), function(index, item) {
                        var alt = $(item).attr('alt') || '';
                        if (alt.indexOf('|left') != -1) {
                            $(item).addClass('img-left');
                        } else if (alt.indexOf('|right') != -1) {
                            $(item).addClass('img-right');
                        } else {
                            $(item).addClass('img-center');
                        }
                    });
                }
                //sidebar
                if (isSidebar) {
                    //round avatar
                    _selector.find('img').first().addClass('avatar');
                    //add animation in item
                    $.each(_selector.find('li'), function(index, item) {
                        $(item).addClass('sidebar-item');
                    });
                }
                //处理图片链接
                $.each(_selector.find('img'), function(index, item) {
                    var $e = $(item);
                    if ($e.attr('src').indexOf('__IMG__') == 0) {
                        $e.attr('src', $e.attr('src').replace('__IMG__', img_root));
                    } else {
                        //适配相对路径
                        var path = resolvePath(getPageBase(p_url), $e.attr('src'));
                        $e.attr('src', path);
                    }
                });

                if (selector == '#sidebar-page'){
                    hook('loaded-sidebar-page');
                }else if (selector == '#main-page'){
                    hook('loaded-main-page');
                }else if (selector == '#main-page-footer'){
                    hook('loaded-main-page-footer')
                }
            },
            error:function(err) {
                if (err.status === 404) {
                    if (file_path === 'footer.md') {
                        console.log('没有找到footer.md! 建议在p/目录下建立footer.md 文件来添加底部信息！');
                        return;
                    }
                    load('#main-page', '404.md', false, '/' + app_name + '/');
                    hook('page-not-found', {
                        selector: selector,
                        path: file_path
                    })
                }
            }
        })
    }

    function read_config(callback) {
        $.ajax({
            method: 'GET',
            url: 'config.json',
            success: function(data) {
                app_name = data.app_name || app_name;
                img_root = data.img_root || img_root;
                var description = data.description || "";

                markdown_root = data.markdown_root || markdown_root;
                blog_base = '/' + app_name + '/' + markdown_root + '/';

                $('meta[name=description]').first().attr('content', description);
                callback();
            },
            error: function(err) {
                alert('读取配置有误');
            }
        })
    }

    function main() {
        read_config(function() {
            //加载侧边菜单栏
            hook('before-load-sidebar-page');
            load('#sidebar-page', 'sidebar.md', true);
            //加载内容页页脚
            hook('before-load-main-page-footer');
            load('#main-page-footer', 'footer.md');
            //加载主内容页
            if (location.search.indexOf('&') !== -1) {
                cur_md_path = location.search.slice(1, location.search.indexOf('&'));
            } else {
                cur_md_path = location.search.slice(1, location.search.length);
            }
            //部分服务器会在后面追加'/',例如：?a/b/cxx.md/
            if (cur_md_path.charAt(cur_md_path.length - 1) === '/') {
                cur_md_path = cur_md_path.slice(0, location.search.length - 2);
            }
            if (cur_md_path === '' || !isMarkdownFile(cur_md_path)) {
                hook('before-load-main-page');
                load('#main-page', 'home.md');
            } else {
                hook('before-load-main-page');
                load('#main-page', cur_md_path);
            }
        });
    }

    main();

})(Zepto);<commit_msg>update blog.js<commit_after>/**
 * blog.js - the core of silentor
 * author: Jayin Ton
 * version: 1.5.4
 * License:Apache 2.0
 */
(function($) {
    'use strict';
    var app_name = 'quex521.github.io';
    var blog_base = '';
    var img_root = 'img';
    var markdown_root = 'p';
    //当前请求的markdown文件
    var cur_md_path = '';
    /*是否是http:// 如果是，那么这是资源文件,如果否，说明这是要处理的a标签*/
    function isAbsolute(url) {
            return url.indexOf('//') !== -1;
        }
        /*获得相对目录*/
    function getPageBase(url) {
            return url.slice(0, url.lastIndexOf('/') + 1);
        }
        /*判断加载的路径是否是markdown文件*/
    function isMarkdownFile(url) {
            return url.toLowerCase().indexOf('.md') !== -1 || url.toLowerCase().indexOf('.markdown') !== -1;
        }
        //获得markdown的文件名,用作标题
    function getMarkdownTitle(file_path) {
            if (!isMarkdownFile(file_path)) {
                return app_name;
            } else {
                var real_file_name = file_path;
                if (hasFolder(file_path)) {
                    real_file_name = file_path.slice(file_path.lastIndexOf('/') + 1, file_path.length);
                }
                return real_file_name.split('.')[0];
            }
        }
    //Event Hook
    function hook(hook_name, data){
        $(window).trigger(hook_name, data);
    }
        // resolve 路径
    function resolvePath(from, to) {
            if (from[from.length - 1] == '/') {
                from = from.substring(0, from.length - 1);
            }
            var froms = from.split('/');
            var tos = to.split('/');
            if (tos[0] == '.') {
                tos.splice(0, 1);
            } else if (tos[0] == '..') {
                froms.splice(froms.length - 1, 1);
                tos.splice(0, 1);
            } else if (tos[0].indexOf('http') != -1 || tos[0].indexOf('https') != -1) {
                return to;
            } else {
                return froms.join('/') + "/" + tos.join('/');
            }
            return resolvePath(froms.join('/'), tos.join('/'));
        }
        /**
         * @param selector 选择器
         * @param  file_path 文件路径
         * @param  isSidebar 是否是左边导航栏
         * @param  baseUrl 基准url
         */
    function load(selector, file_path, isSidebar, baseUrl) {
        var baseUrl = baseUrl || blog_base;
        var isSidebar = isSidebar || false;

        var p_url = baseUrl + file_path;

        $.ajax({
            type: 'GET',
            url: p_url,
            success: function(data){
                marked.setOptions({
                    highlight: function(code) {
                        return hljs.highlightAuto(code).value;
                    }
                });
                var _selector = $(selector);
                _selector.html(marked(data));

                //处理所有href
                _selector.find('[href]').each(function() {
                    var $element = $(this);
                    var url = $element.attr('href');

                    if (isAbsolute(url)) {
                        $element.attr('target', '_blank');
                    }

                    // sidebar
                    if (isSidebar && isMarkdownFile(url)) {
                        $element.attr('href', '?' + url);

                    }

                    //main page
                    if (!isAbsolute(url) && !isSidebar && isMarkdownFile(url)) {
                        var new_url = getPageBase(cur_md_path);
                        //上一级目录
                        if (url.indexOf('../') == 0) {
                            //处理相对路径
                            new_url = resolvePath(getPageBase(cur_md_path), url);
                        } else if (url.indexOf('__P__') == 0) {
                            //文章根目录`p/`下
                            new_url = url.replace('__P__/', '');
                        } else {
                            //当前目录
                            new_url = new_url + url.replace('./', '');
                        }
                        $element.attr('href', '?' + new_url);
                    }
                });
                //main-page
                if (!isSidebar) {
                    //change title
                    var mainTitle = $('#main-page').find('h1, h2, h3, h4, h5, h6').first().text();
                    $('title').text(mainTitle);

                    //图片位置
                    $.each(_selector.find('img'), function(index, item) {
                        var alt = $(item).attr('alt') || '';
                        if (alt.indexOf('|left') != -1) {
                            $(item).addClass('img-left');
                        } else if (alt.indexOf('|right') != -1) {
                            $(item).addClass('img-right');
                        } else {
                            $(item).addClass('img-center');
                        }
                    });
                }
                //sidebar
                if (isSidebar) {
                    //round avatar
                    _selector.find('img').first().addClass('avatar');
                    //add animation in item
                    $.each(_selector.find('li'), function(index, item) {
                        $(item).addClass('sidebar-item');
                    });
                }
                //处理图片链接
                $.each(_selector.find('img'), function(index, item) {
                    var $e = $(item);
                    if ($e.attr('src').indexOf('__IMG__') == 0) {
                        $e.attr('src', $e.attr('src').replace('__IMG__', img_root));
                    } else {
                        //适配相对路径
                        var path = resolvePath(getPageBase(p_url), $e.attr('src'));
                        $e.attr('src', path);
                    }
                });

                if (selector == '#sidebar-page'){
                    hook('loaded-sidebar-page');
                }else if (selector == '#main-page'){
                    hook('loaded-main-page');
                }else if (selector == '#main-page-footer'){
                    hook('loaded-main-page-footer')
                }
            },
            error:function(err) {
                if (err.status === 404) {
                    if (file_path === 'footer.md') {
                        console.log('没有找到footer.md! 建议在p/目录下建立footer.md 文件来添加底部信息！');
                        return;
                    }
                    load('#main-page', '404.md', false, '/' + app_name + '/');
                    hook('page-not-found', {
                        selector: selector,
                        path: file_path
                    })
                }
            }
        })
    }

    function read_config(callback) {
        $.ajax({
            method: 'GET',
            url: 'config.json',
            success: function(data) {
                app_name = data.app_name || app_name;
                img_root = data.img_root || img_root;
                var description = data.description || "";

                markdown_root = data.markdown_root || markdown_root;
                blog_base = '/' + app_name + '/' + markdown_root + '/';

                $('meta[name=description]').first().attr('content', description);
                callback();
            },
            error: function(err) {
                alert('读取配置有误');
            }
        })
    }

    function main() {
        read_config(function() {
            //加载侧边菜单栏
            hook('before-load-sidebar-page');
            load('#sidebar-page', 'sidebar.md', true);
            //加载内容页页脚
            hook('before-load-main-page-footer');
            load('#main-page-footer', 'footer.md');
            //加载主内容页
            if (location.search.indexOf('&') !== -1) {
                cur_md_path = location.search.slice(1, location.search.indexOf('&'));
            } else {
                cur_md_path = location.search.slice(1, location.search.length);
            }
            //部分服务器会在后面追加'/',例如：?a/b/cxx.md/
            if (cur_md_path.charAt(cur_md_path.length - 1) === '/') {
                cur_md_path = cur_md_path.slice(0, location.search.length - 2);
            }
            if (cur_md_path === '' || !isMarkdownFile(cur_md_path)) {
                hook('before-load-main-page');
                load('#main-page', 'home.md');
            } else {
                hook('before-load-main-page');
                load('#main-page', cur_md_path);
            }
        });
    }

    main();

})(Zepto);
<|endoftext|>


<commit_before>import React, {PropTypes} from 'react'
import Pane from 'part:@sanity/desk-tool/pane'
import TypePane from './pane/TypePane'
import EditorPane from './pane/EditorPane'
import TypePaneItem from './pane/TypePaneItem.js'
import QueryContainer from 'part:@sanity/base/query-container'

import UrlDocId from './utils/UrlDocId'
import dataAspects from './utils/dataAspects'
import schema from 'part:@sanity/base/schema'
import documentStore from 'part:@sanity/base/datastore/document'
import styles from './styles/SchemaPaneResolver.css'
import Preview from 'part:@sanity/base/preview'
import FullscreenDialog from 'part:@sanity/components/dialogs/fullscreen'
import {withRouterHOC} from 'part:@sanity/base/router'
import elementResizeDetectorMaker from 'element-resize-detector'

// Debounce function on requestAnimationFrame
function debounceRAF(fn) {
  let scheduled
  return function debounced(...args) {
    if (!scheduled) {
      requestAnimationFrame(() => {
        fn.call(this, ...scheduled)
        scheduled = null
      })
    }
    scheduled = args
  }
}

const TYPE_ITEMS = dataAspects.getInferredTypes().map(typeName => ({
  key: typeName,
  name: typeName,
  title: dataAspects.getDisplayName(typeName)
}))

function readListLayoutSettings() {
  return JSON.parse(window.localStorage.getItem('desk-tool.listlayout-settings') || '{}')
}
function writeListLayoutSettings(settings) {
  window.localStorage.setItem('desk-tool.listlayout-settings', JSON.stringify(settings))
}

export default withRouterHOC(class SchemaPaneResolver extends React.PureComponent {
  static propTypes = {
    router: PropTypes.shape({
      state: PropTypes.object
    })
  }

  state = {
    listLayoutSettings: readListLayoutSettings(),
    sorting: '_updatedAt desc',
    navTranslateX: 0,
    editorWidth: '100%',
    editorTranslateX: 0,
    navIsMinimized: false,
    navIsHovered: false,
    navIsClicked: false
  }

  hasBeenResized = false
  oldNavTranslateX = 0

  componentDidMount() {
    this.handleResize()
    window.addEventListener('resize', this.handleResize, false)
  }

  componentWillUnmount() {
    window.removeEventListener('resize', this.handleResize, false)
    this.erd.removeAllListeners(this.navigationElement)
    // this.erd.uninstall(this.navigationElement)
  }

  componentWillMount() {
    this.erd = elementResizeDetectorMaker({strategy: 'scroll'})
    this.checkRedirect()
  }

  componentDidUpdate() {
    this.checkRedirect()
    // Hack
    // Panes needs to be resized after the content is loaded
    // Look at this later
    this.handleResize()
  }

  checkRedirect() {
    const {router} = this.props

    if (router.state.action === 'create' && !router.state.selectedDocumentId) {
      const selectedType = router.state.selectedType
      documentStore.create({_type: `${schema.name}.${selectedType}`}).subscribe(document => {
        router.navigate({
          action: 'edit',
          selectedType: selectedType,
          selectedDocumentId: UrlDocId.encode(document._id)
        }, {replace: true})
      })
    }
  }

  renderTypePaneItem = item => {
    const {selectedType} = this.props.router.state
    const selected = item.name === selectedType
    return (
      <TypePaneItem
        key={item.key}
        selected={selected}
        type={item}
        onClick={this.handleItemClick}
      />
    )
  }

  renderDocumentPaneItem = item => {
    const {selectedType} = this.props.router.state
    const listLayout = this.getListLayoutForType(selectedType)
    const type = schema.get(selectedType)

    return (
      <Preview
        value={item}
        layout={listLayout}
        type={type}
      />
    )
  }

  getDocumentsPane(typeName) {
    const schemaType = schema.get(typeName)

    const query = `${schema.name}.${schemaType.name}[limit: 2000, order: ${this.state.sorting}] {_id, _type}`

    return (
      <QueryContainer query={query} type={schemaType} listLayout={this.getListLayoutForType(schemaType.name)}>
        {({result, loading, error, type, listLayout}) => {
          if (error) {
            return (
              <FullscreenDialog kind="danger" title="An error occurred while loading items" isOpen>
                {error.message}
              </FullscreenDialog>
            )
          }

          // Hack
          // Panes needs to be resized after the content is loaded
          // Look at this later
          if (!this.hasBeenResized) {
            this.handleResize()
          }

          const items = result && result.documents && result.documents.map(item => {
            item.stateLink = {selectedType: type.name, action: 'edit', selectedDocumentId: UrlDocId.encode(item._id)}
            return item
          })
          return (
            <Pane
              contentType="documents"
              type={type}
              loading={loading}
              items={items}
              renderItem={this.renderDocumentPaneItem}
              onSetListLayout={this.handleSetListLayout}
              onSetSorting={this.handleSetSort}
              listLayout={listLayout}
            />
          )
        }}
      </QueryContainer>
    )
  }

  handleSetListLayout = listLayout => {
    const {selectedType} = this.props.router.state
    const nextSettings = Object.assign(readListLayoutSettings(), {
      [selectedType]: listLayout
    })
    writeListLayoutSettings(nextSettings)
    this.setState({listLayoutSettings: nextSettings})
  }

  getListLayoutForType(type) {
    return this.state.listLayoutSettings[type] || 'default'
  }

  handleSetSort = sorting => {
    this.setState({
      sorting: sorting
    })
  }

  setContainerElement = element => {
    this.containerElement = element
  }

  setNavigationElement = element => {
    this.navigationElement = element
    if (this.navigationElement) {
      this.erd.listenTo(this.navigationElement, el => {
        this.handleResize()
      })
    }
  }

  setEditorPaneElement = element => {
    this.editorPaneElement = element
  }

  canReposition = () => {
    const {selectedType, selectedDocumentId} = this.props.router.state
    return !!(this.navigationElement && this.editorPaneElement && this.containerElement && selectedType && selectedDocumentId)
  }

  resetPosition = () => {
    this.setState({
      navTranslateX: 0,
      editorWidth: '100%',
      navIsMinimized: false,
      editorTranslateX: 0
    })
  }

  handleResize = debounceRAF(() => {
    // Todo optimize later so that we dont resize when we dont need to
    // if (this.initialWindowWidth === window.innerWidth) {
    //   return
    // }

    // Uses the css the determine if it should reposition with an Media Query
    // check if the window has resized
    const computedStyle = window.getComputedStyle(this.containerElement, '::before')
    const contentValue = computedStyle.getPropertyValue('content')
    this.shouldReposition = contentValue === '"shouldReposition"' || contentValue === 'shouldReposition' // Is quoted

    if (!this.shouldReposition || this.state.navIsHovered) {
      return false
    }

    // Setup
    this.navWidth = this.navigationElement.offsetWidth
    const editorPaneWidth = this.editorPaneElement.offsetWidth
    const containerWidth = this.containerElement.offsetWidth
    let navTranslateX = 0
    let editorWidth = `${containerWidth - this.navWidth}px`

    // Setting dimensions based on font-size.
    this.padding = parseInt(window.getComputedStyle(document.body).fontSize, 10) * 3
    const editorPaneMinWidth = parseInt(window.getComputedStyle(this.editorPaneElement, null).minWidth, 10)

    const diff = containerWidth - editorPaneWidth - this.navWidth

    // Check if we need to push the navbar outside view
    if (diff <= 0) {
      navTranslateX = Math.min(containerWidth - editorPaneMinWidth - this.navWidth, 0)
      editorWidth = `${containerWidth - navTranslateX - this.navWidth}px`
    }

    // Set states that triggers re-render
    this.setState({
      navTranslateX: navTranslateX,
      navIsMinimized: navTranslateX < 0,
      editorWidth: editorWidth
    })

    return false
  })

  handlePanesMouseEnter = event => {
    const {navTranslateX} = this.state
    this.oldNavTranslateX = navTranslateX

    if (!this.state.navIsMinimized || !this.shouldReposition) {
      return
    }

    if (!this.canReposition() || !this.shouldReposition || navTranslateX == 0) {
      this.resetPosition()
      return
    }

    const x = 20

    this.setState({
      navTranslateX: navTranslateX + x,
      editorTranslateX: x,
      navIsHovered: true
    })

  }

  handlePanesMouseLeave = event => {
    if (this.shouldReposition) {
      this.setState({
        navTranslateX: Math.min(this.oldNavTranslateX, 0),
        editorTranslateX: 0,
        navIsHovered: false
      })
    }
  }

  handlePanesClick = event => {
    const {navWidth, oldNavTranslateX} = this
    const navVisibleWidth = navWidth + oldNavTranslateX
    if (this.shouldReposition) {
      this.setState({
        navTranslateX: 0,
        editorTranslateX: navWidth - navVisibleWidth,
        navIsMinimized: false,
        navIsClicked: true
      })
    }
  }

  render() {
    const {router} = this.props
    const {selectedType, selectedDocumentId} = router.state
    const {navTranslateX, editorTranslateX, editorWidth, navIsMinimized, navIsClicked} = this.state

    const typesPane = (
      <TypePane
        items={TYPE_ITEMS}
        renderItem={this.renderTypePaneItem}
      />
    )

    const documentsPane = selectedType && this.getDocumentsPane(selectedType)

    return (

        <div
          className={`
            ${navIsMinimized ? styles.navigationPanesContainerIsMinimized : styles.navigationPanesContainer}
            ${navIsClicked ? styles.navigationPanesContainerIsClicked : ''}
          `}
          ref={this.setNavigationElement}
          onClick={this.handlePanesClick}
          onMouseEnter={this.handlePanesMouseEnter}
          onMouseLeave={this.handlePanesMouseLeave}
          style={{
            transform: `translateX(${navTranslateX}px)`
          }}
        >
          {typesPane}
          {documentsPane}

        <div
          className={styles.editorContainer}
          ref={this.setEditorPaneElement}
          id="Sanity_Default_DeskTool_Editor_ScrollContainer"
          style={{
            width: editorWidth,
            transform: `translateX(${editorTranslateX}px)`
          }}
        >
          {
            selectedType && selectedDocumentId && (
              <EditorPane
                documentId={selectedDocumentId && UrlDocId.decode(selectedDocumentId)}
                typeName={selectedType}
              />
            )
          }
          {
            !selectedType && (

Select a type to begin…

            )
          }

    )
  }
})
<commit_msg>[desk-tool] Optimize resize<commit_after>import React, {PropTypes} from 'react'
import Pane from 'part:@sanity/desk-tool/pane'
import TypePane from './pane/TypePane'
import EditorPane from './pane/EditorPane'
import TypePaneItem from './pane/TypePaneItem.js'
import QueryContainer from 'part:@sanity/base/query-container'

import UrlDocId from './utils/UrlDocId'
import dataAspects from './utils/dataAspects'
import schema from 'part:@sanity/base/schema'
import documentStore from 'part:@sanity/base/datastore/document'
import styles from './styles/SchemaPaneResolver.css'
import Preview from 'part:@sanity/base/preview'
import FullscreenDialog from 'part:@sanity/components/dialogs/fullscreen'
import {withRouterHOC} from 'part:@sanity/base/router'
import elementResizeDetectorMaker from 'element-resize-detector'

// Debounce function on requestAnimationFrame
function debounceRAF(fn) {
  let scheduled
  return function debounced(...args) {
    if (!scheduled) {
      requestAnimationFrame(() => {
        fn.call(this, ...scheduled)
        scheduled = null
      })
    }
    scheduled = args
  }
}

const TYPE_ITEMS = dataAspects.getInferredTypes().map(typeName => ({
  key: typeName,
  name: typeName,
  title: dataAspects.getDisplayName(typeName)
}))

function readListLayoutSettings() {
  return JSON.parse(window.localStorage.getItem('desk-tool.listlayout-settings') || '{}')
}
function writeListLayoutSettings(settings) {
  window.localStorage.setItem('desk-tool.listlayout-settings', JSON.stringify(settings))
}

export default withRouterHOC(class SchemaPaneResolver extends React.PureComponent {
  static propTypes = {
    router: PropTypes.shape({
      state: PropTypes.object
    })
  }

  state = {
    listLayoutSettings: readListLayoutSettings(),
    sorting: '_updatedAt desc',
    navTranslateX: 0,
    editorWidth: '100%',
    editorTranslateX: 0,
    navIsMinimized: false,
    navIsHovered: false,
    navIsClicked: false
  }

  hasBeenResized = false
  oldNavTranslateX = 0
  lastNavOffsetWidth = 0

  componentDidMount() {
    this.handleResize()
    window.addEventListener('resize', this.handleResize, false)
  }

  componentWillUnmount() {
    window.removeEventListener('resize', this.handleResize, false)
    this.erd.removeAllListeners(this.navigationElement)
    // this.erd.uninstall(this.navigationElement)
  }

  componentWillMount() {
    this.erd = elementResizeDetectorMaker({strategy: 'scroll'})
    this.checkRedirect()
  }

  componentDidUpdate() {
    this.checkRedirect()
    // Hack
    // Panes needs to be resized after the content is loaded
    // Look at this later
    this.handleResize()
  }

  checkRedirect() {
    const {router} = this.props

    if (router.state.action === 'create' && !router.state.selectedDocumentId) {
      const selectedType = router.state.selectedType
      documentStore.create({_type: `${schema.name}.${selectedType}`}).subscribe(document => {
        router.navigate({
          action: 'edit',
          selectedType: selectedType,
          selectedDocumentId: UrlDocId.encode(document._id)
        }, {replace: true})
      })
    }
  }

  renderTypePaneItem = item => {
    const {selectedType} = this.props.router.state
    const selected = item.name === selectedType
    return (
      <TypePaneItem
        key={item.key}
        selected={selected}
        type={item}
        onClick={this.handleItemClick}
      />
    )
  }

  renderDocumentPaneItem = item => {
    const {selectedType} = this.props.router.state
    const listLayout = this.getListLayoutForType(selectedType)
    const type = schema.get(selectedType)

    return (
      <Preview
        value={item}
        layout={listLayout}
        type={type}
      />
    )
  }

  getDocumentsPane(typeName) {
    const schemaType = schema.get(typeName)

    const query = `${schema.name}.${schemaType.name}[limit: 2000, order: ${this.state.sorting}] {_id, _type}`

    return (
      <QueryContainer query={query} type={schemaType} listLayout={this.getListLayoutForType(schemaType.name)}>
        {({result, loading, error, type, listLayout}) => {
          if (error) {
            return (
              <FullscreenDialog kind="danger" title="An error occurred while loading items" isOpen>
                {error.message}
              </FullscreenDialog>
            )
          }

          // Hack
          // Panes needs to be resized after the content is loaded
          // Look at this later
          if (!this.hasBeenResized) {
            this.handleResize()
          }

          const items = result && result.documents && result.documents.map(item => {
            item.stateLink = {selectedType: type.name, action: 'edit', selectedDocumentId: UrlDocId.encode(item._id)}
            return item
          })
          return (
            <Pane
              contentType="documents"
              type={type}
              loading={loading}
              items={items}
              renderItem={this.renderDocumentPaneItem}
              onSetListLayout={this.handleSetListLayout}
              onSetSorting={this.handleSetSort}
              listLayout={listLayout}
            />
          )
        }}
      </QueryContainer>
    )
  }

  handleSetListLayout = listLayout => {
    const {selectedType} = this.props.router.state
    const nextSettings = Object.assign(readListLayoutSettings(), {
      [selectedType]: listLayout
    })
    writeListLayoutSettings(nextSettings)
    this.setState({listLayoutSettings: nextSettings})
  }

  getListLayoutForType(type) {
    return this.state.listLayoutSettings[type] || 'default'
  }

  handleSetSort = sorting => {
    this.setState({
      sorting: sorting
    })
  }

  setContainerElement = element => {
    this.containerElement = element
  }

  setNavigationElement = element => {
    this.navigationElement = element
    if (this.navigationElement) {
      this.erd.listenTo(this.navigationElement, el => {
        if (el.offsetWidth != this.lastNavOffsetWidth) {
          this.lastNavOffsetWidth = el.offsetWidth
          this.handleResize()
        }
      })
    }
  }

  setEditorPaneElement = element => {
    this.editorPaneElement = element
  }

  canReposition = () => {
    const {selectedType, selectedDocumentId} = this.props.router.state
    return !!(this.navigationElement && this.editorPaneElement && this.containerElement && selectedType && selectedDocumentId)
  }

  resetPosition = () => {
    this.setState({
      navTranslateX: 0,
      editorWidth: '100%',
      navIsMinimized: false,
      editorTranslateX: 0
    })
  }

  handleResize = debounceRAF(() => {
    // Todo optimize later so that we dont resize when we dont need to
    // if (this.initialWindowWidth === window.innerWidth) {
    //   return
    // }

    const {selectedType, selectedDocumentId} = this.props.router.state
    if (!selectedDocumentId || !selectedType) {
      this.resetPosition()
      return
    }

    // Uses the css the determine if it should reposition with an Media Query
    // check if the window has resized
    const computedStyle = window.getComputedStyle(this.containerElement, '::before')
    const contentValue = computedStyle.getPropertyValue('content')
    this.shouldReposition = contentValue === '"shouldReposition"' || contentValue === 'shouldReposition' // Is quoted

    if (!this.shouldReposition || this.state.navIsHovered) {
      return
    }

    // Setup
    this.navWidth = this.navigationElement.offsetWidth
    const editorPaneWidth = this.editorPaneElement.offsetWidth
    const containerWidth = this.containerElement.offsetWidth
    let navTranslateX = 0
    let editorWidth = `${containerWidth - this.navWidth}px`

    // Setting dimensions based on font-size.
    this.padding = parseInt(window.getComputedStyle(document.body).fontSize, 10) * 3
    const editorPaneMinWidth = parseInt(window.getComputedStyle(this.editorPaneElement, null).minWidth, 10)

    const diff = containerWidth - editorPaneWidth - this.navWidth

    // Check if we need to push the navbar outside view
    if (diff <= 0) {
      navTranslateX = Math.min(containerWidth - editorPaneMinWidth - this.navWidth, 0)
      editorWidth = `${containerWidth - navTranslateX - this.navWidth}px`
    }

    // Set states that triggers re-render
    this.setState({
      navTranslateX: navTranslateX,
      navIsMinimized: navTranslateX < 0,
      editorWidth: editorWidth
    })

    return false
  })

  handlePanesMouseEnter = event => {
    const {navTranslateX} = this.state
    this.oldNavTranslateX = navTranslateX

    if (!this.state.navIsMinimized || !this.shouldReposition) {
      return
    }

    if (!this.canReposition() || !this.shouldReposition || navTranslateX == 0) {
      this.resetPosition()
      return
    }

    const x = 20

    this.setState({
      navTranslateX: navTranslateX + x,
      editorTranslateX: x,
      navIsHovered: true
    })

  }

  handlePanesMouseLeave = event => {
    if (this.shouldReposition) {
      this.setState({
        navTranslateX: Math.min(this.oldNavTranslateX, 0),
        editorTranslateX: 0,
        navIsHovered: false
      })
    }
  }

  handlePanesClick = event => {
    const {navWidth, oldNavTranslateX} = this
    const navVisibleWidth = navWidth + oldNavTranslateX
    if (this.shouldReposition) {
      this.setState({
        navTranslateX: 0,
        editorTranslateX: navWidth - navVisibleWidth,
        navIsMinimized: false,
        navIsClicked: true
      })
    }
  }

  render() {
    const {router} = this.props
    const {selectedType, selectedDocumentId} = router.state
    const {navTranslateX, editorTranslateX, editorWidth, navIsMinimized, navIsClicked} = this.state

    const typesPane = (
      <TypePane
        items={TYPE_ITEMS}
        renderItem={this.renderTypePaneItem}
      />
    )

    const documentsPane = selectedType && this.getDocumentsPane(selectedType)

    return (

        <div
          className={`
            ${navIsMinimized ? styles.navigationPanesContainerIsMinimized : styles.navigationPanesContainer}
            ${navIsClicked ? styles.navigationPanesContainerIsClicked : ''}
          `}
          ref={this.setNavigationElement}
          onClick={this.handlePanesClick}
          onMouseEnter={this.handlePanesMouseEnter}
          onMouseLeave={this.handlePanesMouseLeave}
          style={{
            transform: `translateX(${navTranslateX}px)`
          }}
        >
          {typesPane}
          {documentsPane}

        <div
          className={styles.editorContainer}
          ref={this.setEditorPaneElement}
          id="Sanity_Default_DeskTool_Editor_ScrollContainer"
          style={{
            width: editorWidth,
            transform: `translateX(${editorTranslateX}px)`
          }}
        >
          {
            selectedType && selectedDocumentId && (
              <EditorPane
                documentId={selectedDocumentId && UrlDocId.decode(selectedDocumentId)}
                typeName={selectedType}
              />
            )
          }
          {
            !selectedType && (

Select a type to begin…

            )
          }

    )
  }
})
<|endoftext|>


<commit_before>/**
 * Require app SASS/SCSS preprocessor.
 *
 * @see /src/assets/app.scss
 */
require('./assets/sass/app.scss')

//The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import router from './router'
import store from './../storage/store'
import VueFire from 'vuefire'
// Developement mode
import Myfirebase from './../framework/dist/myfirebase'
import VueMaterial from 'vue-material';

// middlewares
import middlewares from './middleware/index'

Vue.config.productionTip = false

// Init VueFire (Firebase real time database)
Vue.use(VueFire)

// init VueMaterial
Vue.use(VueMaterial)

// Register themes with VueMaterial

// Default
Vue.material.registerTheme('default', {
    primary: 'blue',
    accent: 'red',
    warn: 'orange',
    background: 'white'
})

// Login
Vue.material.registerTheme('login', {
    primary: 'indigo',
    accent: 'blue',
    warn: 'black',
    background: 'white'
})

// Init Myfirebase
Vue.use(Myfirebase, {
    store: store,
    router: router,
    middlewares: middlewares
});

// Init Vue app
const app = new Vue({
    router,
    store,
    mounted() {
        /**
         * Register Serviceworkers, these serviceworkers are registred while the vue app is mounted.
         * service-worker.js is for app pre-cache.
         * firebase-messaging-sw.js is for firebase cloud messaging (FCM).
         * @see /public/service-worker.js
         * @see /public/firebase-messaging-sw.js
         * You are free to change them however you like.
         */
        if ('serviceWorker' in navigator) {
            navigator.serviceWorker.register('/service-worker.js')
            navigator.serviceWorker.register('/firebase-messaging-sw.js')
            this.$store.state.messaging
                .requestPermission()
                .then(function() {
                    console.log("Permission accepted")
                })
                .catch(function(error) {
                    console.log("Permission denied")
                })
        }
    },
    data() {
        return {}
    }
}).$mount('#app');<commit_msg>update main.js<commit_after>/**
 * Require app SASS/SCSS preprocessor.
 *
 * @see /src/assets/app.scss
 */
require('@/assets/sass/app.scss')

//The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import router from '@/router'
import store from './../storage/store'
import VueFire from 'vuefire'
import Myfirebase from 'myfirebase'
import VueMaterial from 'vue-material';

// Middlewares
import middlewares from '@/middlewares/index'

Vue.config.productionTip = false

// Init VueFire (Firebase real time database)
Vue.use(VueFire)

// init VueMaterial
Vue.use(VueMaterial)

// Register themes with VueMaterial

// Default
Vue.material.registerTheme('default', {
    primary: 'blue',
    accent: 'red',
    warn: 'orange',
    background: 'white'
})

// Login
Vue.material.registerTheme('login', {
    primary: 'indigo',
    accent: 'blue',
    warn: 'black',
    background: 'white'
})

// Init Myfirebase
Vue.use(Myfirebase, {
    store: store,
    router: router,
    middlewares: middlewares
});

const app = new Vue({
    router,
    store,
    mounted() {
        /**
         * Register Serviceworkers, these serviceworkers are registred while the vue app is mounted.
         * service-worker.js is for app pre-cache.
         * firebase-messaging-sw.js is for firebase cloud messaging (FCM).
         * @see /public/service-worker.js
         * @see /public/firebase-messaging-sw.js
         * You are free to change them however you like.
         */
        if ('serviceWorker' in navigator) {
            navigator.serviceWorker.register('/service-worker.js')
            navigator.serviceWorker.register('/firebase-messaging-sw.js')
            this.$store.state.messaging
                .requestPermission()
                .then(function() {
                    console.log("Permission accepted")
                })
                .catch(function(error) {
                    console.log("Permission denied")
                })
        }
    },
    data() {
        return {}
    }
}).$mount('#app');<|endoftext|>


<commit_before>'use strict'

const AuthError = require('./AuthError')

// Remove the trailing slash so we can always safely append /xyz.
function stripSlash (url) {
  return url.replace(/\/$/, '')
}

module.exports = class RequestClient {
  static VERSION = require('../package.json').version

  constructor (uppy, opts) {
    this.uppy = uppy
    this.opts = opts
    this.onReceiveResponse = this.onReceiveResponse.bind(this)
    this.allowedHeaders = []
    this.preflightDone = false
  }

  get hostname () {
    const { companion } = this.uppy.getState()
    const host = this.opts.companionUrl
    return stripSlash(companion && companion[host] ? companion[host] : host)
  }

  get defaultHeaders () {
    return {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'Uppy-Versions': '@uppy/companion-client=1.0.3'
    }
  }

  headers () {
    return Promise.resolve(
      Object.assign({}, this.defaultHeaders, this.opts.serverHeaders || {})
    )
  }

  _getPostResponseFunc (skip) {
    return (response) => {
      if (!skip) {
        return this.onReceiveResponse(response)
      }

      return response
    }
  }

  onReceiveResponse (response) {
    const state = this.uppy.getState()
    const companion = state.companion || {}
    const host = this.opts.companionUrl
    const headers = response.headers
    // Store the self-identified domain name for the Companion instance we just hit.
    if (headers.has('i-am') && headers.get('i-am') !== companion[host]) {
      this.uppy.setState({
        companion: Object.assign({}, companion, {
          [host]: headers.get('i-am')
        })
      })
    }
    return response
  }

  _getUrl (url) {
    if (/^(https?:|)\/\//.test(url)) {
      return url
    }
    return `${this.hostname}/${url}`
  }

  _json (res) {
    if (res.status === 401) {
      throw new AuthError()
    }

    if (res.status < 200 || res.status > 300) {
      throw new Error(`Failed request to ${res.url}. ${res.statusText}`)
    }
    return res.json()
  }

  preflight (path) {
    return new Promise((resolve, reject) => {
      if (this.preflightDone) {
        return resolve(this.allowedHeaders.slice())
      }

      fetch(this._getUrl(path), {
        method: 'OPTIONS'
      })
        .then((response) => {
          if (response.headers.has('access-control-allow-headers')) {
            const allowedHeaders = response.headers.get('access-control-allow-headers')
              .split(',').map((headerName) => headerName.trim().toLowerCase())
            this.allowedHeaders = this.allowedHeaders.concat(allowedHeaders)
          }
          this.preflightDone = true
          resolve(this.allowedHeaders.slice())
        })
        .catch(reject)
    })
  }

  preflightAndHeaders (path) {
    return new Promise((resolve, reject) => {
      this.preflight(path).then((allowedHeaders) => {
        this.headers().then((headers) => {
          // filter to keep only allowed Headers
          Object.keys(headers).forEach((header) => {
            if (allowedHeaders.indexOf(header.toLowerCase()) === -1) {
              delete headers[header]
            }
          })

          resolve(headers)
        })
      }).catch(reject)
    })
  }

  get (path, skipPostResponse) {
    return new Promise((resolve, reject) => {
      this.preflightAndHeaders(path).then((headers) => {
        fetch(this._getUrl(path), {
          method: 'get',
          headers: headers,
          credentials: 'same-origin'
        })
          .then(this._getPostResponseFunc(skipPostResponse))
          .then((res) => this._json(res).then(resolve))
          .catch((err) => {
            err = err.isAuthError ? err : new Error(`Could not get ${this._getUrl(path)}. ${err}`)
            reject(err)
          })
      })
    })
  }

  post (path, data, skipPostResponse) {
    return new Promise((resolve, reject) => {
      this.preflightAndHeaders(path).then((headers) => {
        fetch(this._getUrl(path), {
          method: 'post',
          headers: headers,
          credentials: 'same-origin',
          body: JSON.stringify(data)
        })
          .then(this._getPostResponseFunc(skipPostResponse))
          .then((res) => this._json(res).then(resolve))
          .catch((err) => {
            err = err.isAuthError ? err : new Error(`Could not post ${this._getUrl(path)}. ${err}`)
            reject(err)
          })
      })
    })
  }

  delete (path, data, skipPostResponse) {
    return new Promise((resolve, reject) => {
      this.preflightAndHeaders(path).then((headers) => {
        fetch(`${this.hostname}/${path}`, {
          method: 'delete',
          headers: headers,
          credentials: 'same-origin',
          body: data ? JSON.stringify(data) : null
        })
          .then(this._getPostResponseFunc(skipPostResponse))
          .then((res) => this._json(res).then(resolve))
          .catch((err) => {
            err = err.isAuthError ? err : new Error(`Could not delete ${this._getUrl(path)}. ${err}`)
            reject(err)
          })
      })
    })
  }
}
<commit_msg>companion-client: set default values to allowed headers<commit_after>'use strict'

const AuthError = require('./AuthError')

// Remove the trailing slash so we can always safely append /xyz.
function stripSlash (url) {
  return url.replace(/\/$/, '')
}

module.exports = class RequestClient {
  static VERSION = require('../package.json').version

  constructor (uppy, opts) {
    this.uppy = uppy
    this.opts = opts
    this.onReceiveResponse = this.onReceiveResponse.bind(this)
    this.allowedHeaders = ['accept', 'content-type']
    this.preflightDone = false
  }

  get hostname () {
    const { companion } = this.uppy.getState()
    const host = this.opts.companionUrl
    return stripSlash(companion && companion[host] ? companion[host] : host)
  }

  get defaultHeaders () {
    return {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'Uppy-Versions': '@uppy/companion-client=1.0.3'
    }
  }

  headers () {
    return Promise.resolve(
      Object.assign({}, this.defaultHeaders, this.opts.serverHeaders || {})
    )
  }

  _getPostResponseFunc (skip) {
    return (response) => {
      if (!skip) {
        return this.onReceiveResponse(response)
      }

      return response
    }
  }

  onReceiveResponse (response) {
    const state = this.uppy.getState()
    const companion = state.companion || {}
    const host = this.opts.companionUrl
    const headers = response.headers
    // Store the self-identified domain name for the Companion instance we just hit.
    if (headers.has('i-am') && headers.get('i-am') !== companion[host]) {
      this.uppy.setState({
        companion: Object.assign({}, companion, {
          [host]: headers.get('i-am')
        })
      })
    }
    return response
  }

  _getUrl (url) {
    if (/^(https?:|)\/\//.test(url)) {
      return url
    }
    return `${this.hostname}/${url}`
  }

  _json (res) {
    if (res.status === 401) {
      throw new AuthError()
    }

    if (res.status < 200 || res.status > 300) {
      throw new Error(`Failed request to ${res.url}. ${res.statusText}`)
    }
    return res.json()
  }

  preflight (path) {
    return new Promise((resolve, reject) => {
      if (this.preflightDone) {
        return resolve(this.allowedHeaders.slice())
      }

      fetch(this._getUrl(path), {
        method: 'OPTIONS'
      })
        .then((response) => {
          if (response.headers.has('access-control-allow-headers')) {
            this.allowedHeaders = response.headers.get('access-control-allow-headers')
              .split(',').map((headerName) => headerName.trim().toLowerCase())
          }
          this.preflightDone = true
          resolve(this.allowedHeaders.slice())
        })
        .catch(reject)
    })
  }

  preflightAndHeaders (path) {
    return new Promise((resolve, reject) => {
      this.preflight(path).then((allowedHeaders) => {
        this.headers().then((headers) => {
          // filter to keep only allowed Headers
          Object.keys(headers).forEach((header) => {
            if (allowedHeaders.indexOf(header.toLowerCase()) === -1) {
              delete headers[header]
            }
          })

          resolve(headers)
        })
      }).catch(reject)
    })
  }

  get (path, skipPostResponse) {
    return new Promise((resolve, reject) => {
      this.preflightAndHeaders(path).then((headers) => {
        fetch(this._getUrl(path), {
          method: 'get',
          headers: headers,
          credentials: 'same-origin'
        })
          .then(this._getPostResponseFunc(skipPostResponse))
          .then((res) => this._json(res).then(resolve))
          .catch((err) => {
            err = err.isAuthError ? err : new Error(`Could not get ${this._getUrl(path)}. ${err}`)
            reject(err)
          })
      })
    })
  }

  post (path, data, skipPostResponse) {
    return new Promise((resolve, reject) => {
      this.preflightAndHeaders(path).then((headers) => {
        fetch(this._getUrl(path), {
          method: 'post',
          headers: headers,
          credentials: 'same-origin',
          body: JSON.stringify(data)
        })
          .then(this._getPostResponseFunc(skipPostResponse))
          .then((res) => this._json(res).then(resolve))
          .catch((err) => {
            err = err.isAuthError ? err : new Error(`Could not post ${this._getUrl(path)}. ${err}`)
            reject(err)
          })
      })
    })
  }

  delete (path, data, skipPostResponse) {
    return new Promise((resolve, reject) => {
      this.preflightAndHeaders(path).then((headers) => {
        fetch(`${this.hostname}/${path}`, {
          method: 'delete',
          headers: headers,
          credentials: 'same-origin',
          body: data ? JSON.stringify(data) : null
        })
          .then(this._getPostResponseFunc(skipPostResponse))
          .then((res) => this._json(res).then(resolve))
          .catch((err) => {
            err = err.isAuthError ? err : new Error(`Could not delete ${this._getUrl(path)}. ${err}`)
            reject(err)
          })
      })
    })
  }
}
<|endoftext|>


<commit_before>import Vue from 'vue'
import Vuetify from 'vuetify'
import 'vuetify/dist/vuetify.css'

import App from './App'
import router from './router'

Vue.use(Vuetify)
Vue.config.productionTip = false

/*
All custom code not in the template start
 */

// i18n
import VueI18n from 'vue-i18n'

Vue.use(VueI18n)
const i18n = new VueI18n({
  locale: 'en', // set locale
  fallbackLocale: 'en',
  //messages, // set locale messages
})

// filters
import moment from 'moment'

Vue.filter('prettyDateFormat', function (uts) {
  return moment.unix(uts).format("DD.MM.YYYY")
})
Vue.filter('prettyDateFrom', function (uts) {
  return moment.unix(uts).from()
})
Vue.filter('prettyTimeDate', function (uts) {
  return moment.unix(uts).format("HH:mm DD.MM.YYYY")
})
Vue.filter('prettyDateTime', function (uts) {
  return moment.unix(uts).format("DD.MM.YYYY HH:mm")
})
Vue.filter('secondsToStartDate', function (seconds) {
  let timestampNow = Math.floor(Date.now() / 1000)
  return moment.unix(timestampNow - seconds).format("DD.MM.YYYY HH:mm")
})
Vue.filter('secondsToDays', function (seconds) {
  return Math.floor(seconds / 86400)
})

// forum widget
import DiscourseLatestPosts from './components/DiscourseLatestPosts'

Vue.component('discourse-latest-posts', DiscourseLatestPosts)

// config
Vue.mixin({
  created: function () {
    this.$config = {
      apiUrl: 'https://demoapi.lvlup.pro/v3/'
    }
  }
})

// store for global data
import Vuex from 'vuex'

Vue.use(Vuex)
const store = new Vuex.Store({
  state: {
    loggedIn: false,
    userJustLoggedOff: false,
    language: 'pl'
  },
  actions: {
    checkLanguage({dispatch, state, commit}) {
      //TODO detect browser language
      if (localStorage.getItem('lang') === null) {
        localStorage.setItem('lang', 'pl')
      }
      commit('language', localStorage.getItem('lang'))
      //TODO check race condition
      setTimeout(() => {
        window.vue.$i18n.locale = localStorage.getItem('lang')
      }, 1)
    },
    changeLanguage({dispatch, state, commit}) {
      let lg = 'en'
      if (state.language === 'en') {
        lg = 'pl'
      }
      commit('language', lg)
      localStorage.setItem('lang', lg)
      //TODO check race condition
      setTimeout(() => {
        window.vue.$i18n.locale = lg
      }, 1)
    },
    checkIfLoggedIn({dispatch, state, commit}) {
      if (localStorage.getItem('token') === null) {
        commit('userLogged', false)
        //TODO redirect to destination page
        //TODO inform user about log in requirement
        //TODO check race condition
        setTimeout(() => {
          window.vue.$router.push('/auth/login')
        }, 1)
      } else {
        //TODO check if token is valid
        commit('userLogged', true)
      }
      commit('userJustLoggedOff', false)
    },
    setLoggedIn({dispatch, state, commit}) {
      commit('userLogged', true)
    },
    setLoggedOut({dispatch, state, commit}) {
      localStorage.removeItem('token')
      console.log(window.vue.$router)
      setTimeout(() => {
        window.vue.$router.push('/auth/login')
      }, 250)
      commit('userLogged', false)
      commit('userJustLoggedOff', true)
    },
    setLoggedOutOld({dispatch, state, commit}) {
      commit('userJustLoggedOff', false)
    },
    whenMounted({dispatch, state, commit}) {
      setTimeout(() => {
        let path = 'https://demomy.lvlup.pro' + window.vue.$router.currentRoute.path
        _paq.push(['setCustomUrl', path])
        _paq.push(['trackPageView'])
      }, 0)
    }
  },
  mutations: {
    userLogged(state, newVal) {
      state.loggedIn = newVal
    },
    userJustLoggedOff(state, newVal) {
      state.userJustLoggedOff = newVal
    },
    language(state, newVal) {
      state.language = newVal
    }
  }
})

import 'font-awesome-webpack'
import 'typeface-roboto'
import 'material-design-icons-iconfont/dist/material-design-icons.css'

/*
All custom code not in the template end
Also see Vue instance below:
 */

/* eslint-disable no-new */
var vm = new Vue({
  el: '#app',
  router,
  render: h => h(App),
  store, //line added to init store
  i18n //line added to init i18n
})

window.vue = vm<commit_msg>Comment with dev server for easier debugging<commit_after>import Vue from 'vue'
import Vuetify from 'vuetify'
import 'vuetify/dist/vuetify.css'

import App from './App'
import router from './router'

Vue.use(Vuetify)
Vue.config.productionTip = false

/*
All custom code not in the template start
 */

// i18n
import VueI18n from 'vue-i18n'

Vue.use(VueI18n)
const i18n = new VueI18n({
  locale: 'en', // set locale
  fallbackLocale: 'en',
  //messages, // set locale messages
})

// filters
import moment from 'moment'

Vue.filter('prettyDateFormat', function (uts) {
  return moment.unix(uts).format("DD.MM.YYYY")
})
Vue.filter('prettyDateFrom', function (uts) {
  return moment.unix(uts).from()
})
Vue.filter('prettyTimeDate', function (uts) {
  return moment.unix(uts).format("HH:mm DD.MM.YYYY")
})
Vue.filter('prettyDateTime', function (uts) {
  return moment.unix(uts).format("DD.MM.YYYY HH:mm")
})
Vue.filter('secondsToStartDate', function (seconds) {
  let timestampNow = Math.floor(Date.now() / 1000)
  return moment.unix(timestampNow - seconds).format("DD.MM.YYYY HH:mm")
})
Vue.filter('secondsToDays', function (seconds) {
  return Math.floor(seconds / 86400)
})

// forum widget
import DiscourseLatestPosts from './components/DiscourseLatestPosts'

Vue.component('discourse-latest-posts', DiscourseLatestPosts)

// config
Vue.mixin({
  created: function () {
    this.$config = {
      apiUrl: 'https://demoapi.lvlup.pro/v3/'
      //apiUrl: 'http://localhost/v3/'
    }
  }
})

// store for global data
import Vuex from 'vuex'

Vue.use(Vuex)
const store = new Vuex.Store({
  state: {
    loggedIn: false,
    userJustLoggedOff: false,
    language: 'pl'
  },
  actions: {
    checkLanguage({dispatch, state, commit}) {
      //TODO detect browser language
      if (localStorage.getItem('lang') === null) {
        localStorage.setItem('lang', 'pl')
      }
      commit('language', localStorage.getItem('lang'))
      //TODO check race condition
      setTimeout(() => {
        window.vue.$i18n.locale = localStorage.getItem('lang')
      }, 1)
    },
    changeLanguage({dispatch, state, commit}) {
      let lg = 'en'
      if (state.language === 'en') {
        lg = 'pl'
      }
      commit('language', lg)
      localStorage.setItem('lang', lg)
      //TODO check race condition
      setTimeout(() => {
        window.vue.$i18n.locale = lg
      }, 1)
    },
    checkIfLoggedIn({dispatch, state, commit}) {
      if (localStorage.getItem('token') === null) {
        commit('userLogged', false)
        //TODO redirect to destination page
        //TODO inform user about log in requirement
        //TODO check race condition
        setTimeout(() => {
          window.vue.$router.push('/auth/login')
        }, 1)
      } else {
        //TODO check if token is valid
        commit('userLogged', true)
      }
      commit('userJustLoggedOff', false)
    },
    setLoggedIn({dispatch, state, commit}) {
      commit('userLogged', true)
    },
    setLoggedOut({dispatch, state, commit}) {
      localStorage.removeItem('token')
      console.log(window.vue.$router)
      setTimeout(() => {
        window.vue.$router.push('/auth/login')
      }, 250)
      commit('userLogged', false)
      commit('userJustLoggedOff', true)
    },
    setLoggedOutOld({dispatch, state, commit}) {
      commit('userJustLoggedOff', false)
    },
    whenMounted({dispatch, state, commit}) {
      setTimeout(() => {
        let path = 'https://demomy.lvlup.pro' + window.vue.$router.currentRoute.path
        _paq.push(['setCustomUrl', path])
        _paq.push(['trackPageView'])
      }, 0)
    }
  },
  mutations: {
    userLogged(state, newVal) {
      state.loggedIn = newVal
    },
    userJustLoggedOff(state, newVal) {
      state.userJustLoggedOff = newVal
    },
    language(state, newVal) {
      state.language = newVal
    }
  }
})

import 'font-awesome-webpack'
import 'typeface-roboto'
import 'material-design-icons-iconfont/dist/material-design-icons.css'

/*
All custom code not in the template end
Also see Vue instance below:
 */

/* eslint-disable no-new */
var vm = new Vue({
  el: '#app',
  router,
  render: h => h(App),
  store, //line added to init store
  i18n //line added to init i18n
})

window.vue = vm
<|endoftext|>


<commit_before>/*
* @license plyfe-widgets Copyright (c) 2014, Plyfe Inc.
* All Rights Reserved.
* Available via the MIT license.
* see: http://github.com/plyfe/plyfe-widgets/LICENSE for details
*/

define(function(require, exports, module) {
  'use strict';

  var utils = require('utils');
  var settings = require('settings');
  var api = require('api');
  var dialog = require('dialog');
  var widget = require('widget');
  var switchboard = require('switchboard');

  var globalInitFnName = 'plyfeAsyncInit';
  // NOTE: Have to use `=== false`. Check build_frags/start.frag for the hack.
  var loadedViaRealAMDLoader = !window.Plyfe || window.Plyfe.amd !== false;

  function PlyfeError(message) {
    this.name = 'PlyfeError';
    this.message = (message || '');
  }
  PlyfeError.prototype = Error.prototype;

  var userToken, widgetDomain, widgetPort, widgetClassName = 'plyfe-widget';

  // Find <script> tag that loaded this code
  var scripts = document.getElementsByTagName('script');
  for(var i = scripts.length - 1; i >= 0; i--) {
    var script = scripts[i];
    if(/\/plyfe-widgets.*?\.js(\?|#|$)/.test(script.src)) {
      settings.api.userToken = utils.dataAttr(script, 'user-token', null);
      settings.api.scheme = utils.dataAttr(script, 'scheme', settings.api.scheme);
      settings.api.domain = utils.dataAttr(script, 'domain', settings.api.domain);
      settings.api.port = +utils.dataAttr(script, 'port') || settings.api.port; // '+' casts to int
      globalInitFnName = utils.dataAttr(script, 'init-name', globalInitFnName);
      break;
    }
  }

  // The globalInitFnName and the auto-creation of widgets doesn't make sense in
  // the AMD load case.
  if(!loadedViaRealAMDLoader) {
    utils.domReady(function() {
      if(window[globalInitFnName] && typeof window[globalInitFnName] === 'function') {
        window[globalInitFnName]();
      } else if(settings.api.userToken) { // We can login the user so load widgets
        login(function() {
          createWidgets();
        });
      }
    });
  }

  function createWidgets() {
    var divs = utils.getElementsByClassName(settings.widget.className);
    for(var i = 0; i < divs.length; i++) {
      widget.create(divs[i]);
    }
  }

  function createWidget(el) {
    return widget.create(el);
  }

  function login(callback) {
    if(!settings.api.userToken) {
      throw new PlyfeError('A userToken must be set before login.');
    }

    var options = {
      withCredentials: true
    };

    if(callback) { options.onSuccess = callback; }

    return api.post('/external_sessions/', { auth_token: settings.api.userToken }, options);
  }

  return {
    settings: settings,
    createWidgets: createWidgets,
    createWidget: createWidget,
    login: login,
  };

});
<commit_msg>Fixed race condition bug where domReady could fire before the main.js returned and created the window.Plyfe object.<commit_after>/*
* @license plyfe-widgets Copyright (c) 2014, Plyfe Inc.
* All Rights Reserved.
* Available via the MIT license.
* see: http://github.com/plyfe/plyfe-widgets/LICENSE for details
*/

define(function(require, exports, module) {
  'use strict';

  var utils = require('utils');
  var settings = require('settings');
  var api = require('api');
  var dialog = require('dialog');
  var widget = require('widget');
  var switchboard = require('switchboard');

  var globalInitFnName = 'plyfeAsyncInit';
  // NOTE: Have to use `=== false`. Check build_frags/start.frag for the hack.
  var loadedViaRealAMDLoader = !window.Plyfe || window.Plyfe.amd !== false;

  function PlyfeError(message) {
    this.name = 'PlyfeError';
    this.message = (message || '');
  }
  PlyfeError.prototype = Error.prototype;

  var userToken, widgetDomain, widgetPort, widgetClassName = 'plyfe-widget';

  // Find <script> tag that loaded this code
  var scripts = document.getElementsByTagName('script');
  for(var i = scripts.length - 1; i >= 0; i--) {
    var script = scripts[i];
    if(/\/plyfe-widgets.*?\.js(\?|#|$)/.test(script.src)) {
      settings.api.userToken = utils.dataAttr(script, 'user-token', null);
      settings.api.scheme = utils.dataAttr(script, 'scheme', settings.api.scheme);
      settings.api.domain = utils.dataAttr(script, 'domain', settings.api.domain);
      settings.api.port = +utils.dataAttr(script, 'port') || settings.api.port; // '+' casts to int
      globalInitFnName = utils.dataAttr(script, 'init-name', globalInitFnName);
      break;
    }
  }

  // The globalInitFnName and the auto-creation of widgets doesn't make sense in
  // the AMD load case.
  if(!loadedViaRealAMDLoader) {
    utils.domReady(function() {
      if(window[globalInitFnName] && typeof window[globalInitFnName] === 'function') {
        // NOTE: Have to use setTimeout to make sure that the rest of the
        // main.js executes first before we call the callback. If we don't then
        // there is a race condition where the window.Plyfe object won't exist
        // yet.
        setTimeout(window[globalInitFnName], 0);
      } else if(settings.api.userToken) { // We can login the user so load widgets
        login(function() {
          createWidgets();
        });
      }
    });
  }

  function createWidgets() {
    var divs = utils.getElementsByClassName(settings.widget.className);
    for(var i = 0; i < divs.length; i++) {
      widget.create(divs[i]);
    }
  }

  function createWidget(el) {
    return widget.create(el);
  }

  function login(callback) {
    if(!settings.api.userToken) {
      throw new PlyfeError('A userToken must be set before login.');
    }

    var options = {
      withCredentials: true
    };

    if(callback) { options.onSuccess = callback; }

    return api.post('/external_sessions/', { auth_token: settings.api.userToken }, options);
  }

  return {
    settings: settings,
    createWidgets: createWidgets,
    createWidget: createWidget,
    login: login,
  };

});
<|endoftext|>


<commit_before>enyo.kind({
  /*
   *  name:
   *  name of this "kind" (optionally namespaced with a .)
   */
  name: 'Slides.Main',
  /*
   *  components:
   *  Array of "kind" objects that compose the layout of your app
   */
  components: [
    {name: 'mainLayout', kind: 'FittableRows', classes: 'enyo-fit', components: [
      {
        kind: 'newness.InfiniteSlidingPane',
        name: 'slidesPanes',
        fit:  true,
        components: [
        ]
      },
      {
        kind: 'onyx.Toolbar',
        layoutKind: 'FittableColumnsLayout',
        components: [
          {kind: 'onyx.Button', allowHtml: true, content: '← Back'},
          {fit: true},
          {kind: 'onyx.Button', allowHtml: true, content: 'Next →'}
        ]
      }
    ]}
  ],

  create: function() {
    this.inherited(arguments);

    var component = { kind: "Slides.Slide", name: "slide1", content: "Hello world!" };
    this.$.slidesPanes.viewTypes.push( component );
    this.$.slidesPanes.push( "slide1" );
  }
});

<commit_msg>Test pane animation with custom component pushing<commit_after>enyo.kind({
  /*
   *  name:
   *  name of this "kind" (optionally namespaced with a .)
   */
  name: 'Slides.Main',
  /*
   *  components:
   *  Array of "kind" objects that compose the layout of your app
   */
  components: [
    {name: 'mainLayout', kind: 'FittableRows', classes: 'enyo-fit', components: [
      {
        kind: 'newness.InfiniteSlidingPane',
        name: 'slidesPanes',
        fit:  true,
        components: [
        ]
      },
      {
        kind: 'onyx.Toolbar',
        layoutKind: 'FittableColumnsLayout',
        components: [
          {kind:      'onyx.Button',
           allowHtml: true,
           content:   '← Back',
           onclick:   'previousSlide'},
          {fit: true},
          {kind:      'onyx.Button',
           allowHtml: true,
           content:   'Next →',
           onclick:   'nextSlide'}
        ]
      }
    ]}
  ],

  create: function() {
    this.inherited(arguments);

    var component = { kind: "Slides.Slide", name: "slide1", content: "Hello world 1!" };
    this.$.slidesPanes.viewTypes.push( component );
    component = { kind: "Slides.Slide", name: "slide2", content: "Hello world 2!" };
    this.$.slidesPanes.viewTypes.push( component );
    component = { kind: "Slides.Slide", name: "slide3", content: "Hello world 3!" };
    this.$.slidesPanes.viewTypes.push( component );
  },

  nextSlide: function() {
    component = this.$.slidesPanes.viewTypes[this.$.slidesPanes.getViewCount()];
    this.$.slidesPanes.push(component.name);
  },

  previousSlide: function() {
    this.$.slidesPanes.getView().pop();
  }
});

<|endoftext|>