<commit_before>/**
 * Creating a sidebar enables you to:
 - create an ordered group of docs
 - render a sidebar for each doc of that group
 - provide next/previous navigation

 The sidebars can be generated from the filesystem, or explicitly defined here.

 Create as many sidebars as you want.
 */

module.exports = {
    documentation: [
        'user_guide/index',
        {
            'First steps 👶': [
                'user_guide/unleash_overview',
                'user_guide/important-concepts',
                'user_guide/quickstart',
            ],
        },
        {
            label: 'How-to guides 🪜',
            type: 'category',
            collapsible: true,
            items: [
                'how-to/how-to-add-strategy-constraints',
                'how-to/how-to-define-custom-context-fields',
                'how-to/how-to-use-custom-strategies',

                'advanced/sso-open-id-connect',
                'advanced/sso-saml',
                'advanced/sso-saml-keycloak',
                'advanced/sso-google',
                'advanced/api_access',
                'user_guide/api-token',
                'user_guide/user-management',
                'user_guide/create_feature_toggle',
            ],
        },
        {
            label: 'Reference documentation 📜',
            type: 'category',
            collapsible: false,
            items: [
                {
                    type: 'category',
                    link: { type: 'doc', id: 'addons/index' },
                    items: [
                        'addons/webhook',
                        'addons/slack',
                        'addons/teams',
                        'addons/datadog',
                    ],
                    label: 'Addons',
                },
                {
                    type: 'category',
                    link: { type: 'doc', id: 'api/index' },
                    label: 'APIs',
                    items: [
                        {
                            'Admin API': [
                                'api/admin/addons',
                                'api/admin/context',
                                'api/admin/events',
                                'api/admin/feature-toggles-v2',
                                'api/admin/feature-types',
                                'api/admin/features',
                                'api/admin/features-archive',
                                'api/admin/metrics',
                                'api/admin/projects',
                                'api/admin/state',
                                'api/admin/strategies',
                                'api/admin/user-admin',
                            ],
                            'Client API': [
                                'api/client/features',
                                'api/client/metrics',
                                'api/client/register',
                            ],
                            'System API': [
                                'api/internal/internal',
                                'api/internal/health',
                            ],
                        },
                        'api/open_api',
                    ],
                },
                {
                    'Client SDKs': [
                        'sdks/index',
                        'sdks/java_sdk',
                        'sdks/node_sdk',
                        'sdks/dot_net_sdk',
                        'sdks/go_sdk',
                        'sdks/python_sdk',
                        'sdks/ruby_sdk',
                        'sdks/php_sdk',
                        'sdks/unleash-proxy',
                        'sdks/android_proxy_sdk',
                        'sdks/proxy-javascript',
                        'sdks/proxy-react',
                        'sdks/proxy-ios',
                        {
                            label: 'Community SDKs',
                            type: 'link',
                            href: '/sdks#community-sdks',
                        },
                    ],
                    'Unleash concepts': [
                        'user_guide/activation_strategy',
                        'user_guide/control_rollout',
                        'user_guide/projects',
                        'user_guide/environments',
                        'user_guide/unleash_context',
                        'user_guide/rbac',
                        'user_guide/technical_debt',
                        'advanced/strategy_constraints',
                        'advanced/custom_activation_strategy',
                        'advanced/feature_toggle_types',
                        'advanced/toggle_variants',
                        'advanced/stickiness',
                        'advanced/archived_toggles',
                        'advanced/audit_log',
                        'advanced/tags',
                        'advanced/enterprise-authentication',
                    ],
                    'Deploy and manage Unleash': [
                        'deploy/getting_started',
                        'deploy/configuring_unleash',
                        'deploy/securing_unleash',
                        'deploy/email',
                        'deploy/google_auth',
                        'deploy/database-setup',
                        'deploy/database_backup',
                        'deploy/migration_guide',
                        'deploy/import_export',
                    ],
                },
                {
                    type: 'category',
                    label: 'Integrations',
                    link: { type: 'doc', id: 'integrations/integrations' },
                    items: [
                        {
                            'Jira Server': [
                                'integrations/jira_server_plugin_installation',
                                'integrations/jira_server_plugin_usage',
                            ],
                        },
                        ,
                    ],
                },
            ],
        },
        {
            'Topic guides 🤓': ['topics/a-b-testing'],
        },
    ],
};
<commit_msg>docs: group sdks by server/client<commit_after>/**
 * Creating a sidebar enables you to:
 - create an ordered group of docs
 - render a sidebar for each doc of that group
 - provide next/previous navigation

 The sidebars can be generated from the filesystem, or explicitly defined here.

 Create as many sidebars as you want.
 */

module.exports = {
    documentation: [
        'user_guide/index',
        {
            'First steps 👶': [
                'user_guide/unleash_overview',
                'user_guide/important-concepts',
                'user_guide/quickstart',
            ],
        },
        {
            label: 'How-to guides 🪜',
            type: 'category',
            collapsible: true,
            items: [
                'how-to/how-to-add-strategy-constraints',
                'how-to/how-to-define-custom-context-fields',
                'how-to/how-to-use-custom-strategies',

                'advanced/sso-open-id-connect',
                'advanced/sso-saml',
                'advanced/sso-saml-keycloak',
                'advanced/sso-google',
                'advanced/api_access',
                'user_guide/api-token',
                'user_guide/user-management',
                'user_guide/create_feature_toggle',
            ],
        },
        {
            label: 'Reference documentation 📜',
            type: 'category',
            collapsible: false,
            items: [
                {
                    type: 'category',
                    link: { type: 'doc', id: 'addons/index' },
                    items: [
                        'addons/webhook',
                        'addons/slack',
                        'addons/teams',
                        'addons/datadog',
                    ],
                    label: 'Addons',
                },
                {
                    type: 'category',
                    link: { type: 'doc', id: 'api/index' },
                    label: 'APIs',
                    items: [
                        {
                            'Admin API': [
                                'api/admin/addons',
                                'api/admin/context',
                                'api/admin/events',
                                'api/admin/feature-toggles-v2',
                                'api/admin/feature-types',
                                'api/admin/features',
                                'api/admin/features-archive',
                                'api/admin/metrics',
                                'api/admin/projects',
                                'api/admin/state',
                                'api/admin/strategies',
                                'api/admin/user-admin',
                            ],
                            'Client API': [
                                'api/client/features',
                                'api/client/metrics',
                                'api/client/register',
                            ],
                            'System API': [
                                'api/internal/internal',
                                'api/internal/health',
                            ],
                        },
                        'api/open_api',
                    ],
                },
                {
                    'Unleash SDKs': [
                        'sdks/index',
                        'sdks/unleash-proxy',
                        {
                            type: 'category',
                            collapsible: false,
                            label: 'Server-side SDKs',
                            items: [
                                'sdks/go_sdk',
                                'sdks/java_sdk',
                                'sdks/node_sdk',
                                'sdks/php_sdk',
                                'sdks/python_sdk',
                                'sdks/ruby_sdk',
                                {
                                    type: 'link',
                                    href: 'https://github.com/unleash/unleash-client-rust',
                                    label: 'Rust SDK',
                                },
                                'sdks/dot_net_sdk',
                            ],
                        },
                        {
                            type: 'category',
                            collapsible: false,
                            label: 'Client-side SDKs',
                            items: [
                                'sdks/android_proxy_sdk',
                                'sdks/proxy-ios',
                                'sdks/proxy-javascript',
                                'sdks/proxy-react',
                            ],
                        },
                        {
                            type: 'link',
                            label: 'Community SDKs',
                            href: '/sdks#community-sdks',
                        },
                    ],
                    'Unleash concepts': [
                        'user_guide/activation_strategy',
                        'advanced/archived_toggles',
                        'advanced/audit_log',
                        'advanced/custom_activation_strategy',
                        'user_guide/environments',
                        'advanced/feature_toggle_types',
                        'advanced/toggle_variants',
                        'user_guide/projects',
                        'user_guide/rbac',
                        'advanced/enterprise-authentication',
                        'advanced/stickiness',
                        'advanced/strategy_constraints',
                        'advanced/tags',
                        'user_guide/technical_debt',
                        'user_guide/unleash_context',
                    ],
                    'Deploy and manage Unleash': [
                        'deploy/getting_started',
                        'deploy/configuring_unleash',
                        'deploy/securing_unleash',
                        'deploy/email',
                        'deploy/google_auth',
                        'deploy/database-setup',
                        'deploy/database_backup',
                        'deploy/migration_guide',
                        'deploy/import_export',
                    ],
                },
                {
                    type: 'category',
                    label: 'Integrations',
                    link: { type: 'doc', id: 'integrations/integrations' },
                    items: [
                        {
                            'Jira Server': [
                                'integrations/jira_server_plugin_installation',
                                'integrations/jira_server_plugin_usage',
                            ],
                        },
                        ,
                    ],
                },
            ],
        },
        {
            'Topic guides 🤓': ['topics/a-b-testing'],
        },
    ],
};
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react';

import listener from './globalEventListener';
import { hideMenu } from './actions';
import { cssClasses, callIfExists } from './helpers';

export default class ContextMenu extends Component {
    static propTypes = {
        id: PropTypes.string.isRequired,
        onHide: PropTypes.func,
        onShow: PropTypes.func
    };

    constructor(props) {
        super(props);

        this.state = {
            x: 0,
            y: 0,
            isVisible: false
        };
    }

    componentDidMount() {
        this.listenId = listener.register(this.handleShow, this.handleHide);
    }

    componentDidUpdate() {
        if (this.state.isVisible) {
            const wrapper = window.requestAnimationFrame || setTimeout;

            wrapper(() => {
                const {x, y} = this.state;

                const {top, left} = this.getMenuPosition(x, y);

                wrapper(() => {
                    this.menu.style.top = `${top}px`;
                    this.menu.style.left = `${left}px`;
                    this.menu.classList.add(cssClasses.menuVisible);
                });
            });
        } else {
            this.menu.classList.remove(cssClasses.menuVisible);
        }
    }

    componentWillUnmount() {
        if (this.listenId) {
            listener.unregister(this.listenId);
        }

        this.unregisterHandlers();
    }

    registerHandlers = () => {
        document.addEventListener('mousedown', this.handleOutsideClick);
        document.addEventListener('ontouchstart', this.handleOutsideClick);
        document.addEventListener('scroll', this.handleHide);
        document.addEventListener('contextmenu', this.handleHide);
        window.addEventListener('resize', this.handleHide);
    }

    unregisterHandlers = () => {
        document.removeEventListener('mousedown', this.handleOutsideClick);
        document.removeEventListener('ontouchstart', this.handleOutsideClick);
        document.removeEventListener('scroll', this.handleHide);
        document.removeEventListener('contextmenu', this.handleHide);
        window.removeEventListener('resize', this.handleHide);
    }

    handleShow = (e) => {
        if (e.detail.id !== this.props.id) return;

        const { x, y } = e.detail.position;

        this.setState({isVisible: true, x, y});
        this.registerHandlers();
        callIfExists(this.props.onShow, e);
    }

    handleHide = (e) => {
        this.unregisterHandlers();
        this.setState({isVisible: false});
        callIfExists(this.props.onHide, e);
    }

    handleOutsideClick = (e) => {
        if (!this.menu.contains(e.target)) hideMenu();
    }

    getMenuPosition = (x, y) => {
        const {scrollTop: scrollX, scrollLeft: scrollY} = document.documentElement;
        const { innerWidth, innerHeight } = window;
        const rect = this.menu.getBoundingClientRect();
        const menuStyles = {
            top: y + scrollY,
            left: x + scrollX
        };

        if (y + rect.height > innerHeight) {
            menuStyles.top -= rect.height;
        }

        if (x + rect.width > innerWidth) {
            menuStyles.left -= rect.width;
        }

        if (menuStyles.top < 0) {
            menuStyles.top = (rect.height < innerHeight) ? (innerHeight - rect.height) / 2 : 0;
        }

        if (menuStyles.left < 0) {
            menuStyles.left = (rect.width < innerWidth) ? (innerWidth - rect.width) / 2 : 0;
        }

        return menuStyles;
    }

    menuRef = (c) => {
        this.menu = c;
    }

    render() {
        const { children } = this.props;
        const { top, left } = this.state;
        const style = {position: 'fixed', top, left};

        return (
            <nav ref={this.menuRef} style={style} className={cssClasses.menu}
                onContextMenu={this.handleHide}>
                {children}
            </nav>
        );
    }
}
<commit_msg>Add minimun styling to hide menu<commit_after>import React, { Component, PropTypes } from 'react';

import listener from './globalEventListener';
import { hideMenu } from './actions';
import { cssClasses, callIfExists } from './helpers';

export default class ContextMenu extends Component {
    static propTypes = {
        id: PropTypes.string.isRequired,
        onHide: PropTypes.func,
        onShow: PropTypes.func
    };

    constructor(props) {
        super(props);

        this.state = {
            x: 0,
            y: 0,
            isVisible: false
        };
    }

    componentDidMount() {
        this.listenId = listener.register(this.handleShow, this.handleHide);
    }

    componentDidUpdate() {
        if (this.state.isVisible) {
            const wrapper = window.requestAnimationFrame || setTimeout;

            wrapper(() => {
                const {x, y} = this.state;

                const {top, left} = this.getMenuPosition(x, y);

                wrapper(() => {
                    this.menu.style.top = `${top}px`;
                    this.menu.style.left = `${left}px`;
                    this.menu.style.opacity = 1;
                    this.menu.style.pointerEvents = 'auto';
                    this.menu.classList.add(cssClasses.menuVisible);
                });
            });
        } else {
            this.menu.style.opacity = 0;
            this.menu.style.pointerEvents = 'none';
            this.menu.classList.remove(cssClasses.menuVisible);
        }
    }

    componentWillUnmount() {
        if (this.listenId) {
            listener.unregister(this.listenId);
        }

        this.unregisterHandlers();
    }

    registerHandlers = () => {
        document.addEventListener('mousedown', this.handleOutsideClick);
        document.addEventListener('ontouchstart', this.handleOutsideClick);
        document.addEventListener('scroll', this.handleHide);
        document.addEventListener('contextmenu', this.handleHide);
        window.addEventListener('resize', this.handleHide);
    }

    unregisterHandlers = () => {
        document.removeEventListener('mousedown', this.handleOutsideClick);
        document.removeEventListener('ontouchstart', this.handleOutsideClick);
        document.removeEventListener('scroll', this.handleHide);
        document.removeEventListener('contextmenu', this.handleHide);
        window.removeEventListener('resize', this.handleHide);
    }

    handleShow = (e) => {
        if (e.detail.id !== this.props.id) return;

        const { x, y } = e.detail.position;

        this.setState({isVisible: true, x, y});
        this.registerHandlers();
        callIfExists(this.props.onShow, e);
    }

    handleHide = (e) => {
        this.unregisterHandlers();
        this.setState({isVisible: false});
        callIfExists(this.props.onHide, e);
    }

    handleOutsideClick = (e) => {
        if (!this.menu.contains(e.target)) hideMenu();
    }

    getMenuPosition = (x, y) => {
        const {scrollTop: scrollX, scrollLeft: scrollY} = document.documentElement;
        const { innerWidth, innerHeight } = window;
        const rect = this.menu.getBoundingClientRect();
        const menuStyles = {
            top: y + scrollY,
            left: x + scrollX
        };

        if (y + rect.height > innerHeight) {
            menuStyles.top -= rect.height;
        }

        if (x + rect.width > innerWidth) {
            menuStyles.left -= rect.width;
        }

        if (menuStyles.top < 0) {
            menuStyles.top = (rect.height < innerHeight) ? (innerHeight - rect.height) / 2 : 0;
        }

        if (menuStyles.left < 0) {
            menuStyles.left = (rect.width < innerWidth) ? (innerWidth - rect.width) / 2 : 0;
        }

        return menuStyles;
    }

    menuRef = (c) => {
        this.menu = c;
    }

    render() {
        const { children } = this.props;
        const { top, left } = this.state;
        const style = {position: 'fixed', top, left, opacity: 0, pointerEvents: 'none'};

        return (
            <nav ref={this.menuRef} style={style} className={cssClasses.menu}
                onContextMenu={this.handleHide}>
                {children}
            </nav>
        );
    }
}
<|endoftext|>


<commit_before>'use strict';

streamaApp.directive('streamaVideoPlayer', [
  'uploadService', 'apiService', 'localStorageService', '$timeout',
  function (uploadService, apiService, localStorageService, $timeout) {

    return {
      restrict: 'AE',
      templateUrl: 'streama-video-player.htm',
      scope: {
        options: '='
      },

      link: function ($scope, $elem, $attrs) {
        var video = $elem.find('video')[0];
        $elem.addClass('nocursor');

        $scope.isMobile = false; //initiate as false
        // device detection
        if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|ipad|iris|kindle|Android|Silk|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(navigator.userAgent)
          || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(navigator.userAgent.substr(0,4))){
          $scope.isMobile = true;
        }

        console.log('%c options', 'color: deeppink; font-weight: bold; text-shadow: 0 0 5px deeppink;', $scope.options);
        $scope.loading = true;
        $scope.initialPlay = false;

        var controlDisplayTimeout;
        var overlayTimeout;

        $scope.volumeLevel = localStorageService.get('volumeLevel') || 5;

        video.oncanplay = function () {
          if(!$scope.initialPlay){
            $scope.canplay = true;
            console.log('%c oncanplay', 'color: deeppink; font-weight: bold; text-shadow: 0 0 5px deeppink;');
            $scope.loading = false;
            if(!$scope.isMobile){
              $scope.play();
            }else{
              $scope.pause();
            }
            $scope.videoDuration = video.duration;
            video.currentTime = $scope.options.customStartingTime || 0;
            $scope.currentTime = video.currentTime;
            $scope.initialPlay = true;
            if($scope.options.videoTrack){
              video.textTracks[0].mode = "hidden";
            }
          }
        };

        video.onwaiting = function() {
          $scope.loading = true;
        };

        video.onplaying = function() {
          $scope.loading = false;
        };

        video.onerror = function(){
          if(!video.duration && !$scope.initialPlay){
            $scope.options.onError();
          }
        };

        video.ontimeupdate = function(){
          $scope.currentTime = video.currentTime;
        };

        $scope.toggleSelectEpisodes = function (episodes) {
          $scope.options.selectedEpisodes = episodes;
        };

        $scope.createNewPlayerSession = function () {
          $scope.options.onSocketSessionCreate();
        };

        $scope.toggleTextTrack = function () {
          $scope.isTextTrackVisible = !$scope.isTextTrackVisible;

          if($scope.isTextTrackVisible){
            video.textTracks[0].mode = "showing";
          }else{
            video.textTracks[0].mode = "hidden";
          }
        };

        Mousetrap.bind('space', function() {
          if($scope.playing){
            $scope.pause();
          }else{
            $scope.play();
          }
          $scope.$apply();
        });

        //$scope.controlsVisible = true;
        $scope.showControls = function () {
          $elem.removeClass('nocursor');
          $timeout.cancel(controlDisplayTimeout);
          $timeout.cancel(overlayTimeout);
          $scope.controlsVisible = true;
          $scope.overlayVisible = false;

          controlDisplayTimeout = $timeout(function(){
            $scope.controlsVisible = false;

            if(!$scope.playing){
              overlayTimeout = $timeout(function () {
                if(!$scope.playing){
                  $scope.overlayVisible = true;
                }
              }, 5000);
            }else{
              $elem.addClass('nocursor');
            }

          }, 2000);
        };

        $scope.playerVolumeToggle = function () {
          if($scope.volumeLevel == 0){
            $scope.volumeLevel = 6;
          }else{
            $scope.volumeLevel = 0;
          }
        };

        $scope.scrubberOptions = {
          orientation: 'horizontal',
          min: 0,
          max: 255,
          range: 'min',
          change: function (e, slider) {
          },
          stop: function (e, slider) {
            video.currentTime = slider.value;
            $scope.currentTime = slider.value;
            $scope.options.onTimeChange(slider, $scope.videoDuration);
          }
        };

        var setVolue = function (slider) {
          var volume = slider.value / 10;
          video.volume = volume;
          if($scope.options.rememberVolumeSetting){
            localStorageService.set('volumeLevel', $scope.volumeLevel);
          }
        };

        $scope.play = function (socketData) {
          video.play();
          $scope.playing = true;
          $scope.options.onPlay(video, socketData);
        };

        $scope.pause = function (socketData) {
          video.pause();
          $scope.playing = false;
          $scope.options.onPause(video, socketData);
        };

        $scope.next = $scope.options.onNext;

        $scope.volumeScrubberOptions = {
          orientation: 'vertical',
          range: 'min',
          change: function (e, slider) {
            setVolue(slider);
          },
          slide: function (e, slider) {
            setVolue(slider);
          }
        };

        $scope.closeVideo = function () {
          $scope.options.onClose();
        };

        $scope.fullScreen = function () {
          $scope.isFullScreen = !$scope.isFullScreen;
          var docElm;
          var docElmClose;
          if($scope.isMobile){
            docElm = video;
          }else{
            docElm = document.documentElement;
            docElmClose = document;
          }

          if($scope.isFullScreen){
            if (docElm.requestFullscreen) {
              docElm.requestFullscreen();
            }
            else if (docElm.mozRequestFullScreen) {
              docElm.mozRequestFullScreen();
            }
            else if (docElm.webkitRequestFullScreen) {
              docElm.webkitRequestFullScreen();
            }
            else if (docElm.msRequestFullscreen) {
              docElm.msRequestFullscreen();
            }
          }

          else{
            if (docElmClose.exitFullscreen) {
              docElmClose.exitFullscreen();
            }
            else if (docElmClose.mozCancelFullScreen) {
              docElmClose.mozCancelFullScreen();
            }
            else if (docElmClose.webkitExitFullscreen) {
              docElmClose.webkitExitFullscreen();
            }
            else if (docElmClose.msExitFullscreen) {
              docElmClose.msExitFullscreen();
            }
          }
        };

        $scope.$on('triggerVideoPlay', function (e, data) {
          $scope.play(data);
        });
        $scope.$on('triggerVideoPause', function (e, data) {
          $scope.pause(data);
        });
        $scope.$on('triggerVideoTimeChange', function (e, data) {
          video.currentTime = data.currentPlayerTime;
          $scope.currentTime = data.currentPlayerTime;
        });

        $scope.$on('$destroy', function() {
          console.log("destroy");
          video.pause();
          video.src = '';
          $elem.find('video').children('source').prop('src', '');
          $elem.find('video').remove().length = 0;
        });
      }
    }
  }]);
<commit_msg>reduced delay for contol hide fixed bug: hide overlay when video is played (vor instance via space-bar or via websocket session)<commit_after>'use strict';

streamaApp.directive('streamaVideoPlayer', [
  'uploadService', 'apiService', 'localStorageService', '$timeout',
  function (uploadService, apiService, localStorageService, $timeout) {

    return {
      restrict: 'AE',
      templateUrl: 'streama-video-player.htm',
      scope: {
        options: '='
      },

      link: function ($scope, $elem, $attrs) {
        var video = $elem.find('video')[0];
        $elem.addClass('nocursor');

        $scope.isMobile = false; //initiate as false
        // device detection
        if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|ipad|iris|kindle|Android|Silk|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(navigator.userAgent)
          || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(navigator.userAgent.substr(0,4))){
          $scope.isMobile = true;
        }

        console.log('%c options', 'color: deeppink; font-weight: bold; text-shadow: 0 0 5px deeppink;', $scope.options);
        $scope.loading = true;
        $scope.initialPlay = false;

        var controlDisplayTimeout;
        var overlayTimeout;

        $scope.volumeLevel = localStorageService.get('volumeLevel') || 5;

        video.oncanplay = function () {
          if(!$scope.initialPlay){
            $scope.canplay = true;
            console.log('%c oncanplay', 'color: deeppink; font-weight: bold; text-shadow: 0 0 5px deeppink;');
            $scope.loading = false;
            if(!$scope.isMobile){
              $scope.play();
            }else{
              $scope.pause();
            }
            $scope.videoDuration = video.duration;
            video.currentTime = $scope.options.customStartingTime || 0;
            $scope.currentTime = video.currentTime;
            $scope.initialPlay = true;
            if($scope.options.videoTrack){
              video.textTracks[0].mode = "hidden";
            }
          }
        };

        video.onwaiting = function() {
          $scope.loading = true;
        };

        video.onplaying = function() {
          $scope.loading = false;
        };

        video.onerror = function(){
          if(!video.duration && !$scope.initialPlay){
            $scope.options.onError();
          }
        };

        video.ontimeupdate = function(){
          $scope.currentTime = video.currentTime;
        };

        $scope.toggleSelectEpisodes = function (episodes) {
          $scope.options.selectedEpisodes = episodes;
        };

        $scope.createNewPlayerSession = function () {
          $scope.options.onSocketSessionCreate();
        };

        $scope.toggleTextTrack = function () {
          $scope.isTextTrackVisible = !$scope.isTextTrackVisible;

          if($scope.isTextTrackVisible){
            video.textTracks[0].mode = "showing";
          }else{
            video.textTracks[0].mode = "hidden";
          }
        };

        Mousetrap.bind('space', function() {
          if($scope.playing){
            $scope.pause();
          }else{
            $scope.play();
          }
          $scope.$apply();
        });

        //$scope.controlsVisible = true;
        $scope.showControls = function () {
          $elem.removeClass('nocursor');
          $timeout.cancel(controlDisplayTimeout);
          $timeout.cancel(overlayTimeout);
          $scope.controlsVisible = true;
          $scope.overlayVisible = false;

          controlDisplayTimeout = $timeout(function(){
            $scope.controlsVisible = false;

            if(!$scope.playing){
              overlayTimeout = $timeout(function () {
                if(!$scope.playing){
                  $scope.overlayVisible = true;
                }
              }, 5000);
            }else{
              $elem.addClass('nocursor');
            }

          }, 1000);
        };

        $scope.playerVolumeToggle = function () {
          if($scope.volumeLevel == 0){
            $scope.volumeLevel = 6;
          }else{
            $scope.volumeLevel = 0;
          }
        };

        $scope.scrubberOptions = {
          orientation: 'horizontal',
          min: 0,
          max: 255,
          range: 'min',
          change: function (e, slider) {
          },
          stop: function (e, slider) {
            video.currentTime = slider.value;
            $scope.currentTime = slider.value;
            $scope.options.onTimeChange(slider, $scope.videoDuration);
          }
        };

        var setVolue = function (slider) {
          var volume = slider.value / 10;
          video.volume = volume;
          if($scope.options.rememberVolumeSetting){
            localStorageService.set('volumeLevel', $scope.volumeLevel);
          }
        };

        $scope.play = function (socketData) {
          video.play();
          $scope.playing = true;
          $scope.options.onPlay(video, socketData);
          $scope.overlayVisible = false;
        };

        $scope.pause = function (socketData) {
          video.pause();
          $scope.playing = false;
          $scope.options.onPause(video, socketData);
        };

        $scope.next = $scope.options.onNext;

        $scope.volumeScrubberOptions = {
          orientation: 'vertical',
          range: 'min',
          change: function (e, slider) {
            setVolue(slider);
          },
          slide: function (e, slider) {
            setVolue(slider);
          }
        };

        $scope.closeVideo = function () {
          $scope.options.onClose();
        };

        $scope.fullScreen = function () {
          $scope.isFullScreen = !$scope.isFullScreen;
          var docElm;
          var docElmClose;
          if($scope.isMobile){
            docElm = video;
          }else{
            docElm = document.documentElement;
            docElmClose = document;
          }

          if($scope.isFullScreen){
            if (docElm.requestFullscreen) {
              docElm.requestFullscreen();
            }
            else if (docElm.mozRequestFullScreen) {
              docElm.mozRequestFullScreen();
            }
            else if (docElm.webkitRequestFullScreen) {
              docElm.webkitRequestFullScreen();
            }
            else if (docElm.msRequestFullscreen) {
              docElm.msRequestFullscreen();
            }
          }

          else{
            if (docElmClose.exitFullscreen) {
              docElmClose.exitFullscreen();
            }
            else if (docElmClose.mozCancelFullScreen) {
              docElmClose.mozCancelFullScreen();
            }
            else if (docElmClose.webkitExitFullscreen) {
              docElmClose.webkitExitFullscreen();
            }
            else if (docElmClose.msExitFullscreen) {
              docElmClose.msExitFullscreen();
            }
          }
        };

        $scope.$on('triggerVideoPlay', function (e, data) {
          $scope.play(data);
        });
        $scope.$on('triggerVideoPause', function (e, data) {
          $scope.pause(data);
        });
        $scope.$on('triggerVideoTimeChange', function (e, data) {
          video.currentTime = data.currentPlayerTime;
          $scope.currentTime = data.currentPlayerTime;
        });

        $scope.$on('$destroy', function() {
          console.log("destroy");
          video.pause();
          video.src = '';
          $elem.find('video').children('source').prop('src', '');
          $elem.find('video').remove().length = 0;
        });
      }
    }
  }]);
<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import gql from 'graphql-tag';
import { Query, Mutation } from 'react-apollo';
import { questionsConnectionQuery } from './ListConnector';

export const questionQuery = gql`
  query questionQuery($id: ID!) {
    question(id: $id) {
      id
      type
      wording
      imageUrl
      secondaryWording
      choices {
        id
        text
      }
    }
  }
`;

export const updateQuestionMutation = gql`
  mutation updateQuestionMutation($input: UpdateQuestionInput!) {
    updateQuestion(input: $input) {
      question {
        id
        type
        wording
        imageUrl
        choices {
          id
          text
        }
      }
    }
  }
`;

// Atualiza o cache do Apollo com as alterações realizadas
const updateApolloStore = (proxy, { data: { updateQuestion } }) => {
  try {
    const cacheData = proxy.readQuery({ query: questionsConnectionQuery });
    const questionEdge = {
      cursor: updateQuestion.question.id,
      node: updateQuestion.question,
    };
    cacheData.questionsConnection.unshift(questionEdge);

    proxy.writeQuery({ query: questionsConnectionQuery, cacheData });
  } catch (cacheError) {
    // Do nothing. Questions were not fetched yet.
  }
};

// Maybe put this in responseToFormValues()
const filesHost = 'http://localhost:4000/files';
export const questionWithFullUrl = question => ({
  ...question,
  imageUrl: question.imageUrl,
  // This doesn't seam a very good solution
  imageFullUrl: question.imageUrl ? `${filesHost}/${question.imageUrl}` : null,
});

const QuestionDetailsConnector = ({ children, id }) => (
  <Query query={questionQuery} variables={{ id }}>
    {({ data, error, loading }) => (
      <Mutation mutation={updateQuestionMutation} update={updateApolloStore}>
        {updateQuestion => {
          if (loading) return
Carregando questão...
;
          return children({
            isLoading: loading,
            question: questionWithFullUrl(data.question),
            updateQuestion,
          });
        }}
      </Mutation>
    )}
  </Query>
);

QuestionDetailsConnector.propTypes = {
  children: PropTypes.func.isRequired,
  id: PropTypes.string,
};

export default QuestionDetailsConnector;
<commit_msg>Add TODO comment to DetailsConnector<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import gql from 'graphql-tag';
import { Query, Mutation } from 'react-apollo';
import { questionsConnectionQuery } from './ListConnector';

export const questionQuery = gql`
  query questionQuery($id: ID!) {
    question(id: $id) {
      id
      type
      wording
      imageUrl
      secondaryWording
      choices {
        id
        text
      }
    }
  }
`;

export const updateQuestionMutation = gql`
  mutation updateQuestionMutation($input: UpdateQuestionInput!) {
    updateQuestion(input: $input) {
      question {
        id
        type
        wording
        imageUrl
        choices {
          id
          text
        }
      }
    }
  }
`;

// Atualiza o cache do Apollo com as alterações realizadas
const updateApolloStore = (proxy, { data: { updateQuestion } }) => {
  try {
    const cacheData = proxy.readQuery({ query: questionsConnectionQuery });
    const questionEdge = {
      cursor: updateQuestion.question.id,
      node: updateQuestion.question,
    };
    cacheData.questionsConnection.unshift(questionEdge);

    proxy.writeQuery({ query: questionsConnectionQuery, cacheData });
  } catch (cacheError) {
    // Do nothing. Questions were not fetched yet.
  }
};

// Maybe put this in responseToFormValues()
const filesHost = 'http://localhost:4000/files';
export const questionWithFullUrl = question => ({
  ...question,
  imageUrl: question.imageUrl,
  // This doesn't seam a very good solution
  imageFullUrl: question.imageUrl ? `${filesHost}/${question.imageUrl}` : null,
});

const QuestionDetailsConnector = ({ children, id }) => (
  <Query query={questionQuery} variables={{ id }}>
    {({ data, error, loading }) => (
      <Mutation mutation={updateQuestionMutation} update={updateApolloStore}>
        {updateQuestion => {
          if (loading) return
Carregando questão...
;
          // TODO: Handle error when the backend is down

          return children({
            isLoading: loading,
            question: questionWithFullUrl(data.question),
            updateQuestion,
          });
        }}
      </Mutation>
    )}
  </Query>
);

QuestionDetailsConnector.propTypes = {
  children: PropTypes.func.isRequired,
  id: PropTypes.string,
};

export default QuestionDetailsConnector;
<|endoftext|>


<commit_before>/* globals require */
var gulp = require('gulp');
var del = require('del');
var gulpLoadPlugins = require('gulp-load-plugins');
var plugins = gulpLoadPlugins();

gulp.task('clean', function (done) {
  del(['dist/*']).then(paths => {
    console.log('Deleted files and folders:\n', paths.join('\n')); // eslint-disable-line no-console, no-undef
    done();
  });
});

gulp.task('inject', ['src'], function () {
  return gulp.src('dist/main.jscad')
    .pipe(plugins.inject(gulp.src(['!dist/main.jscad', 'dist/*.js*']), {
      relative: true,
      starttag: '// include:js',
      endtag: '// endinject',
      transform: function (filepath) {
        return 'include(\'' + filepath + '\');';
      }
    }))
    .pipe(gulp.dest('dist'));
});

gulp.task('src', ['lib'], function () {
  return gulp.src(['!node_modules', '*.jscad'])
    .pipe(plugins.plumber())
    .pipe(gulp.dest('dist'));
});

gulp.task('lib', function () {
  return gulp.src('node_modules/**/jscad.json')
    .pipe(plugins.plumber())
    .pipe(plugins.jscadFiles())
    .pipe(plugins.flatten())
    .pipe(gulp.dest('dist'));
});

gulp.task('default', ['clean', 'lib', 'src', 'inject'], function () {
  plugins.watch(['!dist/*', '**/*.jscad'], plugins.batch(function (events, done) {
    gulp.start('inject', done);
  }));
});<commit_msg>Added `node_modules` to watch list<commit_after>/* globals require */
var gulp = require('gulp');
var del = require('del');
var gulpLoadPlugins = require('gulp-load-plugins');
var plugins = gulpLoadPlugins();

gulp.task('clean', function (done) {
    del(['dist/*']).then(paths => {
        console.log('Deleted files and folders:\n', paths.join('\n')); // eslint-disable-line no-console, no-undef
        done();
    });
});

gulp.task('inject', ['src'], function () {
    return gulp.src('dist/main.jscad')
        .pipe(plugins.inject(gulp.src(['!dist/main.jscad', 'dist/*.js*']), {
            relative: true,
            starttag: '// include:js',
            endtag: '// endinject',
            transform: function (filepath) {
                return 'include(\'' + filepath + '\');';
            }
        }))
        .pipe(gulp.dest('dist'));
});

gulp.task('src', ['lib'], function () {
    return gulp.src(['!node_modules', '*.jscad'])
        .pipe(plugins.plumber())
        .pipe(gulp.dest('dist'));
});

gulp.task('lib', function () {
    return gulp.src('node_modules/**/jscad.json')
        .pipe(plugins.plumber())
        .pipe(plugins.jscadFiles())
        .pipe(plugins.flatten())
        .pipe(gulp.dest('dist'));
});

gulp.task('default', ['clean', 'lib', 'src', 'inject'], function () {
    plugins.watch(['!dist/*', '**/*.jscad', 'node_modules/'], {
        verbose: true,
        followSymlinks: true
    }, plugins.batch(function (events, done) {
        gulp.start('inject', done);
    }));
});
<|endoftext|>


<commit_before>import React, { cloneElement, Component, PropTypes } from 'react';
import Dock from 'react-dock';
import { POSITIONS } from './constants';
import { toggleVisibility, changePosition, changeSize } from './actions';
import reducer from './reducers';

export default class DockMonitor extends Component {
  static reducer = reducer;

  static propTypes = {
    defaultPosition: PropTypes.oneOf(POSITIONS).isRequired,
    defaultIsVisible: PropTypes.bool.isRequired,
    defaultSize: PropTypes.number.isRequired,
    toggleVisibilityKey: PropTypes.string.isRequired,
    changePositionKey: PropTypes.string.isRequired,
    children: PropTypes.element,

    dispatch: PropTypes.func,
    monitorState: PropTypes.shape({
      position: PropTypes.oneOf(POSITIONS).isRequired,
      size: PropTypes.number.isRequired,
      isVisible: PropTypes.bool.isRequired,
      childMonitorState: PropTypes.any
    })
  };

  static defaultProps = {
    defaultIsVisible: true,
    defaultPosition: 'right',
    defaultSize: 0.3
  };

  constructor(props) {
    super(props);
    this.handleKeyDown = this.handleKeyDown.bind(this);
    this.handleSizeChange = this.handleSizeChange.bind(this);
  }

  componentDidMount() {
    window.addEventListener('keydown', this.handleKeyDown);
  }

  componentWillUnmount() {
    window.removeEventListener('keydown', this.handleKeyDown);
  }

  handleKeyDown(e) {
    if (!e.ctrlKey) {
      return;
    }
    e.preventDefault();

    const key = event.keyCode || event.which;
    const char = String.fromCharCode(key);
    switch (char.toUpperCase()) {
    case this.props.toggleVisibilityKey.toUpperCase():
      this.props.dispatch(toggleVisibility());
      break;
    case this.props.changePositionKey.toUpperCase():
      this.props.dispatch(changePosition());
      break;
    default:
      break;
    }
  }

  handleSizeChange(requestedSize) {
    this.props.dispatch(changeSize(requestedSize));
  }

  render() {
    const { monitorState, children, ...rest } = this.props;
    const { position, isVisible, size } = monitorState;
    const childProps = {
      ...rest,
      monitorState: monitorState.childMonitorState
    };

    return (
      <Dock position={position}
            isVisible={isVisible}
            size={size}
            onSizeChange={this.handleSizeChange}
            dimMode='none'>
        {cloneElement(children, childProps)}
      </Dock>
    );
  }
}
<commit_msg>Fix global event reference<commit_after>import React, { cloneElement, Component, PropTypes } from 'react';
import Dock from 'react-dock';
import { POSITIONS } from './constants';
import { toggleVisibility, changePosition, changeSize } from './actions';
import reducer from './reducers';

export default class DockMonitor extends Component {
  static reducer = reducer;

  static propTypes = {
    defaultPosition: PropTypes.oneOf(POSITIONS).isRequired,
    defaultIsVisible: PropTypes.bool.isRequired,
    defaultSize: PropTypes.number.isRequired,
    toggleVisibilityKey: PropTypes.string.isRequired,
    changePositionKey: PropTypes.string.isRequired,
    children: PropTypes.element,

    dispatch: PropTypes.func,
    monitorState: PropTypes.shape({
      position: PropTypes.oneOf(POSITIONS).isRequired,
      size: PropTypes.number.isRequired,
      isVisible: PropTypes.bool.isRequired,
      childMonitorState: PropTypes.any
    })
  };

  static defaultProps = {
    defaultIsVisible: true,
    defaultPosition: 'right',
    defaultSize: 0.3
  };

  constructor(props) {
    super(props);
    this.handleKeyDown = this.handleKeyDown.bind(this);
    this.handleSizeChange = this.handleSizeChange.bind(this);
  }

  componentDidMount() {
    window.addEventListener('keydown', this.handleKeyDown);
  }

  componentWillUnmount() {
    window.removeEventListener('keydown', this.handleKeyDown);
  }

  handleKeyDown(e) {
    if (!e.ctrlKey) {
      return;
    }
    e.preventDefault();

    const key = e.keyCode || e.which;
    const char = String.fromCharCode(key);
    switch (char.toUpperCase()) {
    case this.props.toggleVisibilityKey.toUpperCase():
      this.props.dispatch(toggleVisibility());
      break;
    case this.props.changePositionKey.toUpperCase():
      this.props.dispatch(changePosition());
      break;
    default:
      break;
    }
  }

  handleSizeChange(requestedSize) {
    this.props.dispatch(changeSize(requestedSize));
  }

  render() {
    const { monitorState, children, ...rest } = this.props;
    const { position, isVisible, size } = monitorState;
    const childProps = {
      ...rest,
      monitorState: monitorState.childMonitorState
    };

    return (
      <Dock position={position}
            isVisible={isVisible}
            size={size}
            onSizeChange={this.handleSizeChange}
            dimMode='none'>
        {cloneElement(children, childProps)}
      </Dock>
    );
  }
}
<|endoftext|>


<commit_before>/**
 * @fileoverview Provides a layer info service. That service is a function used
 * to retrieve and display the info (metadata) for a layer.
 */

goog.provide('app.ShowLayerinfo');

goog.require('ngeo.CreatePopup');

/**
 * @typedef {function(ol.layer.Layer)}
 */
app.ShowLayerinfo;

/**
 * @param {angularGettext.Catalog} gettextCatalog Gettext catalog.
 * @param {angular.$http} $http Angular $http service
 * @param {angular.$sce} $sce Angular $sce service
 * @param {ngeo.CreatePopup} ngeoCreatePopup Ngeo popup factory service
 * @return {app.ShowLayerinfo} The show layer info function.
 * @ngInject
 */
app.showLayerinfoFactory = function(gettextCatalog,
                                    $http,
                                    $sce,
                                    ngeoCreatePopup) {

  /**
   * @type {ngeo.Popup}
   */
  var popup = ngeoCreatePopup();
  /**
   * @type {Object.<!string, Object.<string, !angular.$q.Promise>>}
   * @private
   */
  var promises_ = {};

  return (
      /**
       * @param {ol.layer.Layer} layer The layer
       */
      function(layer) {
        var title = /** @type {string} */ (layer.get('label'));
        var local_metadata = /** @type {Object.<string>} */
            (layer.get('metadata'));
        var metadata_uid = /** @type {string} */
            (local_metadata['metadata_id']);
        popup.setTitle(title);
        var current_language = /** @type {string} */
            (gettextCatalog.currentLanguage);

        if (!(current_language in promises_)) {
          promises_[current_language] = {};
        }

        if (!(metadata_uid in promises_[current_language])) {
          promises_[current_language][metadata_uid] = $http.jsonp(
              'http://shop.geoportail.lu/Portail/inspire/webservices/getMD.jsp',
              {params: {
                'uid': metadata_uid,
                'lang': current_language,
                'cb': 'JSON_CALLBACK'
              }}).then(
                  angular.bind(this, function(resp) {
                    var remote_metadata = resp.data.root[0];
                    remote_metadata['uid'] = local_metadata['metadata_id'];
                    if ('legend_name' in local_metadata) {
                      remote_metadata['legend_name'] =
                      local_metadata['legend_name'];
                      remote_metadata['legend_url'] = $sce.trustAsResourceUrl(
                          '//wiki.geoportail.lu/doku.php?id=' +
                          gettextCatalog.currentLanguage + ':legend:' +
                          remote_metadata['legend_name'] + '&do=export_html'
                      );
                      remote_metadata['has_legend'] = true;
                    }else {
                      remote_metadata['has_legend'] = false;
                    }
                    remote_metadata['is_error'] = false;
                    remote_metadata['is_short_desc'] = true;
                    showPopup(remote_metadata);
                    return remote_metadata;
                  }));
        }

        promises_[current_language][metadata_uid].then(
            function(remote_metadata) {
              showPopup(remote_metadata);
            },
            function(error) {
              showPopup({'is_error': true});
            });

        /**
         * @param {Object} content Object with metadata information.
         */
        function showPopup(content) {
          popup.setContent(content);
          popup.show();
        };

      });
};

app.module.service('appShowLayerinfo', app.showLayerinfoFactory);
<commit_msg>camelCase notation<commit_after>/**
 * @fileoverview Provides a layer info service. That service is a function used
 * to retrieve and display the info (metadata) for a layer.
 */

goog.provide('app.ShowLayerinfo');

goog.require('ngeo.CreatePopup');

/**
 * @typedef {function(ol.layer.Layer)}
 */
app.ShowLayerinfo;

/**
 * @param {angularGettext.Catalog} gettextCatalog Gettext catalog.
 * @param {angular.$http} $http Angular $http service
 * @param {angular.$sce} $sce Angular $sce service
 * @param {ngeo.CreatePopup} ngeoCreatePopup Ngeo popup factory service
 * @return {app.ShowLayerinfo} The show layer info function.
 * @ngInject
 */
app.showLayerinfoFactory = function(gettextCatalog,
                                    $http,
                                    $sce,
                                    ngeoCreatePopup) {

  /**
   * @type {ngeo.Popup}
   */
  var popup = ngeoCreatePopup();
  /**
   * @type {Object.<!string, Object.<string, !angular.$q.Promise>>}
   * @private
   */
  var promises_ = {};

  return (
      /**
       * @param {ol.layer.Layer} layer The layer
       */
      function(layer) {
        var title = /** @type {string} */ (layer.get('label'));
        var localMetadata = /** @type {Object.<string>} */
            (layer.get('metadata'));
        var metadataUid = /** @type {string} */
            (localMetadata['metadata_id']);
        popup.setTitle(title);
        var currentLanguage = /** @type {string} */
            (gettextCatalog.currentLanguage);

        if (!(currentLanguage in promises_)) {
          promises_[currentLanguage] = {};
        }

        if (!(metadataUid in promises_[currentLanguage])) {
          promises_[currentLanguage][metadataUid] = $http.jsonp(
              'http://shop.geoportail.lu/Portail/inspire/webservices/getMD.jsp',
              {params: {
                'uid': metadataUid,
                'lang': currentLanguage,
                'cb': 'JSON_CALLBACK'
              }}).then(
                  angular.bind(this, function(resp) {
                    var remoteMetadata = resp.data.root[0];
                    remoteMetadata['uid'] = localMetadata['metadata_id'];
                    if ('legend_name' in localMetadata) {
                      remoteMetadata['legend_name'] =
                      localMetadata['legend_name'];
                      remoteMetadata['legend_url'] = $sce.trustAsResourceUrl(
                          '//wiki.geoportail.lu/doku.php?id=' +
                          gettextCatalog.currentLanguage + ':legend:' +
                          remoteMetadata['legend_name'] + '&do=export_html'
                      );
                      remoteMetadata['has_legend'] = true;
                    }else {
                      remoteMetadata['has_legend'] = false;
                    }
                    remoteMetadata['is_error'] = false;
                    remoteMetadata['is_short_desc'] = true;
                    showPopup(remoteMetadata);
                    return remoteMetadata;
                  }));
        }

        promises_[currentLanguage][metadataUid].then(
            function(remoteMetadata) {
              showPopup(remoteMetadata);
            },
            function(error) {
              showPopup({'is_error': true});
            });

        /**
         * @param {Object} content Object with metadata information.
         */
        function showPopup(content) {
          popup.setContent(content);
          popup.show();
        };

      });
};

app.module.service('appShowLayerinfo', app.showLayerinfoFactory);
<|endoftext|>


<commit_before>/**

A constructor function to create new element view objects.

    var elementView = new maria.ElementView();

This constructor function takes three optional arguments.

    var model = new maria.Model();
    var controller = new maria.Controller();
    var myFrameDoc = window.frames['myFrame'].document;
    var elementView =
        new maria.ElementView(model, controller, myFrameDoc);

The null or undefined value can be passed for any of these three
parameters to skip setting it.

The purpose of the third document parameter is to allow the creation
of element views in one window that will be shown in another window.
At least some older browsers do not allow a DOM element created with
one document object to be appended to another document object.

An element view is a view (inheriting from maria.View) and so has
a model and controller. See maria.View for more documentation about
setting and getting the model and controller objects.

What makes maria.ElementView different from the more abstract
maria.View is that an element view has the concept of a "root element"
which is the main DOM element that acts as a container for all the
other DOM elements that are part of the element view.

The DOM is built using the getTemplate method which should return a
fragment of HTML for a single DOM element and its children. By default
the template is just an empty div element. You can redefine or override
this to suit your needs.

    maria.ElementView.prototype.getTemplate = function() {
        return '
' +
                   'hello, ' +
                   'world' +
               '
';
    };

When an element view is created and its HTML template is rendered as
a DOM element, the view will automatically start listening to the DOM
element or its children for the events specified in the map returned
by the getUIActions method. This map is empty by default but you can
redefine or override as necessary and supply the necessary handler
functions which usually delegate to the controller.

    maria.ElementView.prototype.getUIActions = function() {
        return {
            'mouseover .greeting': 'onMouseoverGreeting',
            'click .name'        : 'onClickName'
        };
    };

    maria.ElementView.prototype.onMouseoverGreeting = function(evt) {
        this.getController().onMouseoverGreeting(evt);
    };

    maria.ElementView.prototype.onClickName = function(evt) {
        this.getController().onClickName(evt);
    };

Only a few simple CSS selectors are allowed in the keys of the UI
action map. An id can be used like "#alpha" but this is not
recommended. A class name like ".greeting", a tag name like "div", or
a combination of tag name and class name like "div.greeting" are
acceptable. In almost all cases, a single class name is sufficient and
recommended as the best practice. (If you need more complex selectors
you can use a different query library to replace the Grail library
used by default in Maria.)

You can find an element or multiple elements in a view using the
element view's find and findAll methods.

    elementView.find('.name');   // returns a DOM element
    elementView.findAll('span'); // returns an array

Because maria.View objects are composite views, so are
maria.ElementView objects. This means that sub-element-view objects can
be added to an element view. By default the sub-element-view object's
root DOM element will be added to the parent element view's root
DOM element. You can change the element to which they are added by
redefining or overridding the getContainerEl function.

    maria.ElementView.prototype.getContainerEl = function() {
        return this.find('.name');
    };

A particularly useful pattern is using maria.ElementView as the
"superclass" of your application's element views. The following example
shows how this can be done at a low level for a to-do application. See
maria.ElementView.subclass for a much more compact way to accomplish
the same.

    checkit.TodoView = function() {
        maria.ElementView.apply(this, arguments);
    };
    checkit.TodoView.prototype = maria.create(maria.ElementView.prototype);
    checkit.TodoView.prototype.constructor = checkit.TodoView;
    checkit.TodoView.prototype.getDefaultControllerConstructor = function() {
        return checkit.TodoController;
    };
    checkit.TodoView.prototype.getTemplate = function() {
        return checkit.TodoTemplate;
    };
    checkit.TodoView.prototype.getUIActions = function() {
        return {
            'click     .check'       : 'onClickCheck'     ,
            'dblclick  .todo-content': 'onDblclickDisplay',
            'keyup     .todo-input'  : 'onKeyupInput'     ,
            'keypress  .todo-input'  : 'onKeypressInput'  ,
            'blur      .todo-input'  : 'onBlurInput'
        };
    };
    checkit.TodoView.prototype.onClickCheck = function(evt) {
        this.getController().onClickCheck(evt);
    };
    checkit.TodoView.prototype.onDblclickDisplay = function(evt) {
        this.getController().onDblclickDisplay(evt);
    };
    checkit.TodoView.prototype.onKeyupInput = function(evt) {
        this.getController().onKeyupInput(evt);
    };
    checkit.TodoView.prototype.onKeypressInput = function(evt) {
        this.getController().onKeypressInput(evt);
    };
    checkit.TodoView.prototype.onBlurInput = function(evt) {
        this.getController().onBlurInput(evt);
    };
    checkit.TodoView.prototype.buildData = function() {
        var model = this.getModel();
        var content = model.getContent();
        this.find('.todo-content').innerHTML = checkit.escapeHTML(content);
        this.find('.check').checked = model.isDone();
        aristocrat[model.isDone() ? 'addClass' : 'removeClass'](this.find('.todo'), 'done');
    };
    checkit.TodoView.prototype.update = function() {
        this.buildData();
    };
    checkit.TodoView.prototype.showEdit = function() {
        var input = this.find('.todo-input');
        input.value = this.getModel().getContent();
        aristocrat.addClass(this.find('.todo'), 'editing');
        input.select();
    };
    checkit.TodoView.prototype.showDisplay = function() {
        aristocrat.removeClass(this.find('.todo'), 'editing');
    };
    checkit.TodoView.prototype.getInputValue = function() {
        return this.find('.todo-input').value;
    };
    checkit.TodoView.prototype.showToolTip = function() {
        this.find('.ui-tooltip-top').style.display = 'block';
    };
    checkit.TodoView.prototype.hideToolTip = function() {
        this.find('.ui-tooltip-top').style.display = 'none';
    };

@constructor

@param {maria.Model} [model]

@param {maria.Controller} [controller]

@param {Document} [document]

@extends maria.View

*/
maria.ElementView = function(model, controller, doc) {
    maria.View.call(this, model, controller);
    this.setDocument(doc);
};

maria.ElementView.prototype = maria.create(maria.View.prototype);
maria.ElementView.prototype.constructor = maria.ElementView;

/**

Returns the web page document for the view. This document
is the one used to create elements to be added to the page,
for example.

@return {Document} The document object.

*/
maria.ElementView.prototype.getDocument = function() {
    return this._doc || document;
};

/**

Set the web page document for the view. This document
is the one used to create elements to be added to the page,
for example.

*/
maria.ElementView.prototype.setDocument = function(doc) {
    this._doc = doc;
    var childViews = this.childNodes;
    for (var i = 0, ilen = childViews.length; i < ilen; i++) {
        childViews[i].setDocument(doc);
    }
};

/**

Returns the template for this view used during the build process.

@return {string} The template HTML string.

*/
maria.ElementView.prototype.getTemplate = function() {
    return '

';
};

/**

The UI actions object maps a UI action like a click
on a button with a handler method name. By default,
the handler will be called on the controller of the view.

@return {Object} The UI actions map.

*/
maria.ElementView.prototype.getUIActions = function() {
    return {};
};

/**

Builds the root DOM element for the view from the view's template
returned by `getTemplate`, attaches event handlers to the root
and its descendents as specified by the UI actions map returned
by `getUIActions`, calls the `buildData` method to allow model
values to be inserted into the root DOM element and its descendents,
and calls `buildChildViews`. This construction of the root DOM element
is lazy and only done when this method is called.

@return {Element} The root DOM Element of the view.

*/
maria.ElementView.prototype.build = function() {
    if (!this._rootEl) {
        this.buildTemplate();
        this.buildUIActions();
        this.buildData();
        this.buildChildViews();
    }
    return this._rootEl;
};

/**

Parses the HTML template string returned by `getTemplate` to create a
`DocumentFragment`. The first child of that `DocumentFragment` is set
as the root element of this view. All other sibling elements of the
`DocumentFragment` are discarded.

*/
maria.ElementView.prototype.buildTemplate = function() {
    // parseHTML returns a DocumentFragment so take firstChild as the rootEl
    this._rootEl = arbutus.parseHTML(this.getTemplate(), this.getDocument()).firstChild;
};

(function() {
    var actionRegExp = /^(\S+)\s*(.*)$/;

/**

Attaches event handlers to the root and its descendents as specified
by the UI actions map returned by `getUIActions`.

*/
    maria.ElementView.prototype.buildUIActions = function() {
        var uiActions = this.getUIActions();
        for (var key in uiActions) {
            if (Object.prototype.hasOwnProperty.call(uiActions, key)) {
                var matches = key.match(actionRegExp),
                    eventType = matches[1],
                    selector = matches[2],
                    methodName = uiActions[key],
                    elements = this.findAll(selector, this._rootEl);
                for (var i = 0, ilen = elements.length; i < ilen; i++) {
                    maria.on(elements[i], eventType, this, methodName);
                }
            }
        }
    };

}());

/**

Does nothing by default. To be overridden by subclasses.

The intended use of this method is to populate the built root DOM element
and its descendents with model data.

*/
maria.ElementView.prototype.buildData = function() {
    // to be overridden by concrete ElementView subclasses
};

/*

Used as part of the initial building of the view. If child views have
been added to the view, then these children also built and appened
to the element returned by `getContainerEl`.

*/
maria.ElementView.prototype.buildChildViews = function() {
    var childViews = this.childNodes;
    for (var i = 0, ilen = childViews.length; i < ilen; i++) {
        this.getContainerEl().appendChild(childViews[i].build());
    }
};

/**

See `buildChildViews` for more details.

@return {Element} The DOM Element to which child view's should be attached.

*/
maria.ElementView.prototype.getContainerEl = function() {
    return this.build();
};

/**

Add a new child view before an existing child view. If the `oldChild`
parameter is not supplied then the `newChild` is appened as the last child.

@param {maria.ElementView} newChild The child to be inserted.

@param {maria.ElementView} oldChild The child to insert before.

*/
maria.ElementView.prototype.insertBefore = function(newChild, oldChild) {
    maria.View.prototype.insertBefore.call(this, newChild, oldChild);
    if (this._rootEl) {
        this.getContainerEl().insertBefore(newChild.build(), oldChild ? oldChild.build() : null);
    }
};

/**

Remove an existing child view.

@param {maria.ElementView} oldChild The child to be removed.

*/
maria.ElementView.prototype.removeChild = function(oldChild) {
    maria.View.prototype.removeChild.call(this, oldChild);
    if (this._rootEl) {
        this.getContainerEl().removeChild(oldChild.build());
    }
};

/**

Find the first element in this view that matches the CSS `selector`. The
view's root element can be the result.

By default Maria uses the Grail library as its DOM query engine. This is
to support older browsers that do not have `querySelector`. The Grail
engine only a limited set of simple selectors.

    .class
    tag
    tag.class
    #id

If your application only needs to work in newer browsers you can create
a Maria plugin to use `querySelector` but ensure the root element will
be returned if it matches `selector`.

If your application needs to work in older browsers but you need more
complex CSS `selector` strings then you can create a Maria plugin
to use some libray other than Grail.

@param {string} selector A CSS selector.

@return {Element} The first DOM element matching `selector`.

*/
maria.ElementView.prototype.find = function(selector) {
    return grail.find(selector, this.build());
};

/**

Find all the elements in this view that matches the CSS `selector`. The
view's root element can be in the result set.

See `find` for more details.

@param {string} selector A CSS selector.

@return {Array} An array of the DOM elements matching `selector`.

*/
maria.ElementView.prototype.findAll = function(selector) {
    return grail.findAll(selector, this.build());
};
<commit_msg>Update documentation note. This is not a requirement but a suggestion.<commit_after>/**

A constructor function to create new element view objects.

    var elementView = new maria.ElementView();

This constructor function takes three optional arguments.

    var model = new maria.Model();
    var controller = new maria.Controller();
    var myFrameDoc = window.frames['myFrame'].document;
    var elementView =
        new maria.ElementView(model, controller, myFrameDoc);

The null or undefined value can be passed for any of these three
parameters to skip setting it.

The purpose of the third document parameter is to allow the creation
of element views in one window that will be shown in another window.
At least some older browsers do not allow a DOM element created with
one document object to be appended to another document object.

An element view is a view (inheriting from maria.View) and so has
a model and controller. See maria.View for more documentation about
setting and getting the model and controller objects.

What makes maria.ElementView different from the more abstract
maria.View is that an element view has the concept of a "root element"
which is the main DOM element that acts as a container for all the
other DOM elements that are part of the element view.

The DOM is built using the getTemplate method which should return a
fragment of HTML for a single DOM element and its children. By default
the template is just an empty div element. You can redefine or override
this to suit your needs.

    maria.ElementView.prototype.getTemplate = function() {
        return '
' +
                   'hello, ' +
                   'world' +
               '
';
    };

When an element view is created and its HTML template is rendered as
a DOM element, the view will automatically start listening to the DOM
element or its children for the events specified in the map returned
by the getUIActions method. This map is empty by default but you can
redefine or override as necessary and supply the necessary handler
functions which usually delegate to the controller.

    maria.ElementView.prototype.getUIActions = function() {
        return {
            'mouseover .greeting': 'onMouseoverGreeting',
            'click .name'        : 'onClickName'
        };
    };

    maria.ElementView.prototype.onMouseoverGreeting = function(evt) {
        this.getController().onMouseoverGreeting(evt);
    };

    maria.ElementView.prototype.onClickName = function(evt) {
        this.getController().onClickName(evt);
    };

Only a few simple CSS selectors are allowed in the keys of the UI
action map. An id can be used like "#alpha" but this is not
recommended. A class name like ".greeting", a tag name like "div", or
a combination of tag name and class name like "div.greeting" are
acceptable. In almost all cases, a single class name is sufficient and
recommended as the best practice. (If you need more complex selectors
you can use a different query library to replace the Grail library
used by default in Maria.)

You can find an element or multiple elements in a view using the
element view's find and findAll methods.

    elementView.find('.name');   // returns a DOM element
    elementView.findAll('span'); // returns an array

Because maria.View objects are composite views, so are
maria.ElementView objects. This means that sub-element-view objects can
be added to an element view. By default the sub-element-view object's
root DOM element will be added to the parent element view's root
DOM element. You can change the element to which they are added by
redefining or overridding the getContainerEl function.

    maria.ElementView.prototype.getContainerEl = function() {
        return this.find('.name');
    };

A particularly useful pattern is using maria.ElementView as the
"superclass" of your application's element views. The following example
shows how this can be done at a low level for a to-do application. See
maria.ElementView.subclass for a much more compact way to accomplish
the same.

    checkit.TodoView = function() {
        maria.ElementView.apply(this, arguments);
    };
    checkit.TodoView.prototype = maria.create(maria.ElementView.prototype);
    checkit.TodoView.prototype.constructor = checkit.TodoView;
    checkit.TodoView.prototype.getDefaultControllerConstructor = function() {
        return checkit.TodoController;
    };
    checkit.TodoView.prototype.getTemplate = function() {
        return checkit.TodoTemplate;
    };
    checkit.TodoView.prototype.getUIActions = function() {
        return {
            'click     .check'       : 'onClickCheck'     ,
            'dblclick  .todo-content': 'onDblclickDisplay',
            'keyup     .todo-input'  : 'onKeyupInput'     ,
            'keypress  .todo-input'  : 'onKeypressInput'  ,
            'blur      .todo-input'  : 'onBlurInput'
        };
    };
    checkit.TodoView.prototype.onClickCheck = function(evt) {
        this.getController().onClickCheck(evt);
    };
    checkit.TodoView.prototype.onDblclickDisplay = function(evt) {
        this.getController().onDblclickDisplay(evt);
    };
    checkit.TodoView.prototype.onKeyupInput = function(evt) {
        this.getController().onKeyupInput(evt);
    };
    checkit.TodoView.prototype.onKeypressInput = function(evt) {
        this.getController().onKeypressInput(evt);
    };
    checkit.TodoView.prototype.onBlurInput = function(evt) {
        this.getController().onBlurInput(evt);
    };
    checkit.TodoView.prototype.buildData = function() {
        var model = this.getModel();
        var content = model.getContent();
        this.find('.todo-content').innerHTML = checkit.escapeHTML(content);
        this.find('.check').checked = model.isDone();
        aristocrat[model.isDone() ? 'addClass' : 'removeClass'](this.find('.todo'), 'done');
    };
    checkit.TodoView.prototype.update = function() {
        this.buildData();
    };
    checkit.TodoView.prototype.showEdit = function() {
        var input = this.find('.todo-input');
        input.value = this.getModel().getContent();
        aristocrat.addClass(this.find('.todo'), 'editing');
        input.select();
    };
    checkit.TodoView.prototype.showDisplay = function() {
        aristocrat.removeClass(this.find('.todo'), 'editing');
    };
    checkit.TodoView.prototype.getInputValue = function() {
        return this.find('.todo-input').value;
    };
    checkit.TodoView.prototype.showToolTip = function() {
        this.find('.ui-tooltip-top').style.display = 'block';
    };
    checkit.TodoView.prototype.hideToolTip = function() {
        this.find('.ui-tooltip-top').style.display = 'none';
    };

@constructor

@param {maria.Model} [model]

@param {maria.Controller} [controller]

@param {Document} [document]

@extends maria.View

*/
maria.ElementView = function(model, controller, doc) {
    maria.View.call(this, model, controller);
    this.setDocument(doc);
};

maria.ElementView.prototype = maria.create(maria.View.prototype);
maria.ElementView.prototype.constructor = maria.ElementView;

/**

Returns the web page document for the view. This document
is the one used to create elements to be added to the page,
for example.

@return {Document} The document object.

*/
maria.ElementView.prototype.getDocument = function() {
    return this._doc || document;
};

/**

Set the web page document for the view. This document
is the one used to create elements to be added to the page,
for example.

*/
maria.ElementView.prototype.setDocument = function(doc) {
    this._doc = doc;
    var childViews = this.childNodes;
    for (var i = 0, ilen = childViews.length; i < ilen; i++) {
        childViews[i].setDocument(doc);
    }
};

/**

Returns the template for this view used during the build process.

@return {string} The template HTML string.

*/
maria.ElementView.prototype.getTemplate = function() {
    return '

';
};

/**

The UI actions object maps a UI action like a click
on a button with a handler method name. By default,
the handler will be called on the controller of the view.

@return {Object} The UI actions map.

*/
maria.ElementView.prototype.getUIActions = function() {
    return {};
};

/**

Builds the root DOM element for the view from the view's template
returned by `getTemplate`, attaches event handlers to the root
and its descendents as specified by the UI actions map returned
by `getUIActions`, calls the `buildData` method to allow model
values to be inserted into the root DOM element and its descendents,
and calls `buildChildViews`. This construction of the root DOM element
is lazy and only done when this method is called.

@return {Element} The root DOM Element of the view.

*/
maria.ElementView.prototype.build = function() {
    if (!this._rootEl) {
        this.buildTemplate();
        this.buildUIActions();
        this.buildData();
        this.buildChildViews();
    }
    return this._rootEl;
};

/**

Parses the HTML template string returned by `getTemplate` to create a
`DocumentFragment`. The first child of that `DocumentFragment` is set
as the root element of this view. All other sibling elements of the
`DocumentFragment` are discarded.

*/
maria.ElementView.prototype.buildTemplate = function() {
    // parseHTML returns a DocumentFragment so take firstChild as the rootEl
    this._rootEl = arbutus.parseHTML(this.getTemplate(), this.getDocument()).firstChild;
};

(function() {
    var actionRegExp = /^(\S+)\s*(.*)$/;

/**

Attaches event handlers to the root and its descendents as specified
by the UI actions map returned by `getUIActions`.

*/
    maria.ElementView.prototype.buildUIActions = function() {
        var uiActions = this.getUIActions();
        for (var key in uiActions) {
            if (Object.prototype.hasOwnProperty.call(uiActions, key)) {
                var matches = key.match(actionRegExp),
                    eventType = matches[1],
                    selector = matches[2],
                    methodName = uiActions[key],
                    elements = this.findAll(selector, this._rootEl);
                for (var i = 0, ilen = elements.length; i < ilen; i++) {
                    maria.on(elements[i], eventType, this, methodName);
                }
            }
        }
    };

}());

/**

Does nothing by default. To be overridden by subclasses.

The intended use of this method is to populate the built root DOM element
and its descendents with model data.

*/
maria.ElementView.prototype.buildData = function() {
    // to be overridden by concrete ElementView subclasses
};

/*

Used as part of the initial building of the view. If child views have
been added to the view, then these children also built and appened
to the element returned by `getContainerEl`.

*/
maria.ElementView.prototype.buildChildViews = function() {
    var childViews = this.childNodes;
    for (var i = 0, ilen = childViews.length; i < ilen; i++) {
        this.getContainerEl().appendChild(childViews[i].build());
    }
};

/**

See `buildChildViews` for more details.

@return {Element} The DOM Element to which child view's should be attached.

*/
maria.ElementView.prototype.getContainerEl = function() {
    return this.build();
};

/**

Add a new child view before an existing child view. If the `oldChild`
parameter is not supplied then the `newChild` is appened as the last child.

@param {maria.ElementView} newChild The child to be inserted.

@param {maria.ElementView} oldChild The child to insert before.

*/
maria.ElementView.prototype.insertBefore = function(newChild, oldChild) {
    maria.View.prototype.insertBefore.call(this, newChild, oldChild);
    if (this._rootEl) {
        this.getContainerEl().insertBefore(newChild.build(), oldChild ? oldChild.build() : null);
    }
};

/**

Remove an existing child view.

@param {maria.ElementView} oldChild The child to be removed.

*/
maria.ElementView.prototype.removeChild = function(oldChild) {
    maria.View.prototype.removeChild.call(this, oldChild);
    if (this._rootEl) {
        this.getContainerEl().removeChild(oldChild.build());
    }
};

/**

Find the first element in this view that matches the CSS `selector`. The
view's root element can be the result.

By default Maria uses the Grail library as its DOM query engine. This is
to support older browsers that do not have `querySelector`. The Grail
engine only a limited set of simple selectors.

    .class
    tag
    tag.class
    #id

If your application only needs to work in newer browsers then you can create
a Maria plugin to use `querySelector`. Consider if you want the root element
to be returned if it matches `selector`.

If your application needs to work in older browsers but you need more
complex CSS `selector` strings then you can create a Maria plugin
to use some libray other than Grail.

@param {string} selector A CSS selector.

@return {Element} The first DOM element matching `selector`.

*/
maria.ElementView.prototype.find = function(selector) {
    return grail.find(selector, this.build());
};

/**

Find all the elements in this view that matches the CSS `selector`. The
view's root element can be in the result set.

See `find` for more details.

@param {string} selector A CSS selector.

@return {Array} An array of the DOM elements matching `selector`.

*/
maria.ElementView.prototype.findAll = function(selector) {
    return grail.findAll(selector, this.build());
};
<|endoftext|>


<commit_before>/**
 * Copyright 2015-present Greg Hurrell. All rights reserved.
 * Licensed under the terms of the MIT license.
 *
 * @flow
 */

'use strict';

import React from 'react';
import convert from './convert';

export type Value = {
  base: number;
  value: string;
};
export const ValuePropType = React.PropTypes.shape({
  base: React.PropTypes.number,
  value: React.PropTypes.string,
});

const DIGITS = '0123456789abcdef';

/**
 * Convert from `value` to `base`.
 */
function fromValue(value: ?Value, base: number): string {
  if (value === null) {
    return '';
  } else if (value.base === base) {
    return value.value;
  }
  return convert(value.value, value.base, base);
}

export default class Field extends React.Component {
  static propTypes = {
    base: React.PropTypes.number,
    onValueChange: React.PropTypes.func.required,
    value: ValuePropType,
  };
  static defaultProps = {
    base: 10,
  };

  constructor(props) {
    super(props);
    if (props.base < 2 || props.base > DIGITS.length) {
      throw new Error(
        `base prop must be between 2..${DIGITS.length}`
      );
    }
  }

  _isValid(value: string): boolean {
    const validator = new RegExp(
      `^[${DIGITS.slice(0, this.props.base)}]*$`
    );
    return validator.test(value.trim().toLowerCase());
  }

  _onChange = event => {
    const value = event.currentTarget.value;
    if (this._isValid(value)) {
      this.props.onValueChange({
        base: this.props.base,
        value,
      });
    }
  }

  render() {
    return (
      <input
        className="hextrapolate-input"
        onChange={this._onChange}
        type="text"
        value={fromValue(this.props.value, this.props.base)}
      />
    );
  }
}
<commit_msg>Add copy-to-clipboard functionality<commit_after>/**
 * Copyright 2015-present Greg Hurrell. All rights reserved.
 * Licensed under the terms of the MIT license.
 *
 * @flow
 */

'use strict';

import React from 'react';
import convert from './convert';

export type Value = {
  base: number;
  value: string;
};
export const ValuePropType = React.PropTypes.shape({
  base: React.PropTypes.number,
  value: React.PropTypes.string,
});

const DIGITS = '0123456789abcdef';

/**
 * Convert from `value` to `base`.
 */
function fromValue(value: ?Value, base: number): string {
  if (value === null) {
    return '';
  } else if (value.base === base) {
    return value.value;
  }
  return convert(value.value, value.base, base);
}

export default class Field extends React.Component {
  static propTypes = {
    base: React.PropTypes.number,
    onValueChange: React.PropTypes.func.required,
    value: ValuePropType,
  };
  static defaultProps = {
    base: 10,
  };

  constructor(props) {
    super(props);
    if (props.base < 2 || props.base > DIGITS.length) {
      throw new Error(
        `base prop must be between 2..${DIGITS.length}`
      );
    }
  }

  _isValid(value: string): boolean {
    const validator = new RegExp(
      `^[${DIGITS.slice(0, this.props.base)}]*$`
    );
    return validator.test(value.trim().toLowerCase());
  }

  _onChange = event => {
    const value = event.currentTarget.value;
    if (this._isValid(value)) {
      this.props.onValueChange({
        base: this.props.base,
        value,
      });
    }
  }

  _onCopy = event => {
    const input = React.findDOMNode(this._input);
    input.select();

    try {
      // May throw a SecurityError.
      document.execCommand('copy');
    } catch(error) {
      // Swallow.
    }
  }

  _copyLink = () => {
    // Would check `document.queryCommandSupported('copy')` here, but that
    // doesn't work; see:
    // - https://code.google.com/p/chromium/issues/detail?id=476508
    // - https://github.com/w3c/clipboard-apis/issues/4
    return copy;
  }

  render() {
    return (

        <input
          className="hextrapolate-input"
          onChange={this._onChange}
          ref={ref => this._input = ref}
          type="text"
          value={fromValue(this.props.value, this.props.base)}
        />
        {this._copyLink()}

    );
  }
}
<|endoftext|>