<commit_before>'use strict';

/*global ace*/

import { Provider } from 'react-redux';
import { DevTools, DebugPanel } from 'redux-devtools/lib/react';

import ProfileLink from './components/ProfileLink.react'

/* Directives */
var MetabaseDirectives = angular.module('metabase.directives', []);

MetabaseDirectives.directive('deleteConfirm', [function() {
    return {
        priority: 1,
        terminal: true,
        link: function(scope, element, attr) {
            var msg = attr.ngConfirmClick || "Are you sure?";
            var clickAction = attr.ngClick;
            element.bind('click', function(event) {
                if (window.confirm(msg)) {
                    scope.$eval(clickAction);
                }
            });
        }
    };
}]);

MetabaseDirectives.directive('mbDelayedCall', ['$timeout', function($timeout) {

    function link(scope, element, attr) {
        var delay = attr.delay;
        if (!delay) {
            delay = 8000;
        }

        var func = attr.mbDelayedCall;

        $timeout(() => scope.$eval(func), delay);
    }

    return {
        restrict: 'A',
        link: link
    };
}]);

MetabaseDirectives.directive('mbScrollShadow', [function (){
    return {
        restrict: 'A',
        link: function (scope, element, attr) {
            // grab the raw dom element to check its scroll top
            var raw = element[0];
            element.on('scroll', function () {
                if(raw.scrollTop > 0) {
                    element.addClass('ScrollShadow');
                } else {
                    element.removeClass('ScrollShadow');
                }
            });
        }
    };
}]);

MetabaseDirectives.directive('mbActionButton', ['$timeout', '$compile', function ($timeout, $compile) {

    return {
        restrict: 'A',
        scope: {
            actionFunction: '=mbActionButton'
        },
        link: function link(scope, element, attr) {

            var defaultText = element.text();
            var activeText = attr.activeText;
            var failedText = attr.failedText;
            var successText = attr.successText;

            var fnArg = attr.fnArg;

            var delayedReset = function() {
                // do we need to have this timeout be configurable?
                $timeout(function () {
                    element.text(defaultText);
                    element.removeClass('Button--waiting');
                    element.removeClass('Button--success');
                    element.removeClass('Button--danger');
                    element.removeAttr('disabled');
                }, 5000);
            };

            element.bind('click', function (event) {
                element.text(activeText);
                element.addClass('Button--waiting');

                // activate spinner
                var loadingIcon = angular.element('<mb-loading-icon width="12px" height="12px"></mb-loading-icon>');
                element.append(loadingIcon);
                $compile(loadingIcon)(scope);

                // disable button
                element.attr('disabled', 'true');

                // NOTE: we are assuming the action function is a promise
                var promise = (fnArg) ? scope.actionFunction(fnArg) : scope.actionFunction();

                promise.then(function (result) {
                    element.text(successText);
                    element.removeClass('Button--waiting');
                    element.addClass('Button--success');
                    var checkIcon = angular.element('<mb-icon name="check" width="12px" height="12px"></mb-icon>');
                    element.prepend(checkIcon);
                    $compile(checkIcon)(scope);

                    // re-activate button
                    element.removeAttr('disabled');

                    // timeout, reset to base
                    delayedReset();

                }, function (error) {
                    element.text(failedText);
                    element.removeClass('Button--waiting');
                    element.addClass('Button--danger');

                    // re-activate button
                    element.removeAttr('disabled');

                    // timeout, reset to base
                    delayedReset();
                });
            });
        }
    };
}]);

MetabaseDirectives.directive('mbReduxComponent', ['$timeout', function ($timeout) {
    return {
        restrict: 'A',
        link: function (scope, element, attr) {
            if (scope.monitor) {
                var win = window.open(null, "redux-devtools", "menubar=no,location=no,resizable=yes,scrollbars=no,status=no");
                win.location.reload();
                setTimeout(function() {
                    React.render(
                        <DebugPanel top right bottom left >
                            <DevTools store={scope.store} monitor={scope.monitor} />
                        </DebugPanel>
                    , win.document.body);
                }, 10);
            }

            React.render(
                <Provider store={scope.store}>
                    {() => <scope.Component {...scope.props} />}
                </Provider>,
                element[0]
            );

            scope.$on("$destroy", function() {
                React.unmountComponentAtNode(element[0]);
            });
        }
    };
}]);

MetabaseDirectives.directive('mbReactComponent', ['$timeout', function ($timeout) {
    return {
        restrict: 'A',
        link: function (scope, element, attr) {
            var Component = scope[attr.mbReactComponent];
            delete scope[attr.mbReactComponent];

            function render() {
                var props = {};
                function copyProps(dest, src) {
                    for (var key in src) {
                        copyProp(dest, key, src[key]);
                    }
                }
                function copyProp(dest, key, value) {
                    if (typeof value === "function") {
                        dest[key] = function() {
                            try {
                                return value.apply(this, arguments);
                            } finally {
                                $timeout(() => scope.$digest());
                            }
                        }
                        copyProps(dest[key], value);
                    } else {
                        dest[key] = value;
                    }
                }
                copyProps(props, scope)
                React.render(<Component {...props}/>, element[0]);
            }

            scope.$on("$destroy", function() {
                React.unmountComponentAtNode(element[0]);
            });

            // limit renders to once per animation frame
            var timeout;
            scope.$watch(function() {
                if (!timeout) {
                    timeout = requestAnimationFrame(function() {
                        timeout = null;
                        render();
                    });
                }
            });

            render();
        }
    };
}]);

var NavbarDirectives = angular.module('metabase.navbar.directives', []);

NavbarDirectives.directive('mbProfileLink', [function () {

    return {
        restrict: 'A',
        replace: true,
        template: '

',
        controller: function ($scope) {
            $scope.ProfileLink = ProfileLink
            $scope.userIsSuperuser = false;

            $scope.$watch('user', function (user) {
                if (!user) return;
                // extract a couple informational pieces about user
                $scope.userIsSuperuser = user.is_superuser;
            });
        },
        scope: {
            context: '=',
            user: '='
        },
    };
}]);

var MetabaseACEEditorDirectives = angular.module('metabase.aceeditor.directives', ['ui.ace']);

MetabaseACEEditorDirectives.directive('mbAceSqlEditor', function() {

    function controller($scope, Metabase) {
        $scope.aceLoaded = function(aceEditor) {
            if ($scope.onLoad) {
                var fn = $scope.onLoad();
                if (fn) fn(aceEditor);
            }

            var aceLanguageTools = ace.require('ace/ext/language_tools');
            aceEditor.setOptions({
                enableBasicAutocompletion: true,
                enableSnippets: true,
                enableLiveAutocompletion: true
            });

            aceLanguageTools.addCompleter({
                getCompletions: function(editor, session, pos, prefix, callback) {
                    if (prefix.lengh === 0 || !$scope.database) {
                        console.log("$scope.database is not set, unable to perform autocompletions for ACE Editor :'(");
                        callback(null, []);
                        return;
                    }

                    Metabase.db_autocomplete_suggestions({
                        dbId: $scope.database,
                        prefix: prefix
                    }, function(results) {
                        // transform results of the API call into what ACE expects
                        var js_results = results.map(function(result) {
                            return {
                                name: result[0],
                                value: result[0],
                                meta: result[1]
                            };
                        });
                        callback(null, js_results);

                    }, function(error) {
                        console.log(error);
                        callback(null, []);
                    });
                }
            });

            // focus the editor on load to allow faster editing of query
            aceEditor.focus();
        };
    }

    return {
        restrict: 'E',
        templateUrl: '/app/components/editor/editor.html',
        controller: ['$scope', 'Metabase', controller],
        scope: {
            sql: '=', // the text of the editor itself
            database: '=', // int ID of DB to use for autocompletion
            onLoad: '&onload' // optional callback of the form fn(aceEditor) in case we need a reference to it (e.g. so we can aceEditor.focus())
        }
    };
});
<commit_msg>no longer need these scope vars<commit_after>'use strict';

/*global ace*/

import { Provider } from 'react-redux';
import { DevTools, DebugPanel } from 'redux-devtools/lib/react';

import ProfileLink from './components/ProfileLink.react'

/* Directives */
var MetabaseDirectives = angular.module('metabase.directives', []);

MetabaseDirectives.directive('deleteConfirm', [function() {
    return {
        priority: 1,
        terminal: true,
        link: function(scope, element, attr) {
            var msg = attr.ngConfirmClick || "Are you sure?";
            var clickAction = attr.ngClick;
            element.bind('click', function(event) {
                if (window.confirm(msg)) {
                    scope.$eval(clickAction);
                }
            });
        }
    };
}]);

MetabaseDirectives.directive('mbDelayedCall', ['$timeout', function($timeout) {

    function link(scope, element, attr) {
        var delay = attr.delay;
        if (!delay) {
            delay = 8000;
        }

        var func = attr.mbDelayedCall;

        $timeout(() => scope.$eval(func), delay);
    }

    return {
        restrict: 'A',
        link: link
    };
}]);

MetabaseDirectives.directive('mbScrollShadow', [function (){
    return {
        restrict: 'A',
        link: function (scope, element, attr) {
            // grab the raw dom element to check its scroll top
            var raw = element[0];
            element.on('scroll', function () {
                if(raw.scrollTop > 0) {
                    element.addClass('ScrollShadow');
                } else {
                    element.removeClass('ScrollShadow');
                }
            });
        }
    };
}]);

MetabaseDirectives.directive('mbActionButton', ['$timeout', '$compile', function ($timeout, $compile) {

    return {
        restrict: 'A',
        scope: {
            actionFunction: '=mbActionButton'
        },
        link: function link(scope, element, attr) {

            var defaultText = element.text();
            var activeText = attr.activeText;
            var failedText = attr.failedText;
            var successText = attr.successText;

            var fnArg = attr.fnArg;

            var delayedReset = function() {
                // do we need to have this timeout be configurable?
                $timeout(function () {
                    element.text(defaultText);
                    element.removeClass('Button--waiting');
                    element.removeClass('Button--success');
                    element.removeClass('Button--danger');
                    element.removeAttr('disabled');
                }, 5000);
            };

            element.bind('click', function (event) {
                element.text(activeText);
                element.addClass('Button--waiting');

                // activate spinner
                var loadingIcon = angular.element('<mb-loading-icon width="12px" height="12px"></mb-loading-icon>');
                element.append(loadingIcon);
                $compile(loadingIcon)(scope);

                // disable button
                element.attr('disabled', 'true');

                // NOTE: we are assuming the action function is a promise
                var promise = (fnArg) ? scope.actionFunction(fnArg) : scope.actionFunction();

                promise.then(function (result) {
                    element.text(successText);
                    element.removeClass('Button--waiting');
                    element.addClass('Button--success');
                    var checkIcon = angular.element('<mb-icon name="check" width="12px" height="12px"></mb-icon>');
                    element.prepend(checkIcon);
                    $compile(checkIcon)(scope);

                    // re-activate button
                    element.removeAttr('disabled');

                    // timeout, reset to base
                    delayedReset();

                }, function (error) {
                    element.text(failedText);
                    element.removeClass('Button--waiting');
                    element.addClass('Button--danger');

                    // re-activate button
                    element.removeAttr('disabled');

                    // timeout, reset to base
                    delayedReset();
                });
            });
        }
    };
}]);

MetabaseDirectives.directive('mbReduxComponent', ['$timeout', function ($timeout) {
    return {
        restrict: 'A',
        link: function (scope, element, attr) {
            if (scope.monitor) {
                var win = window.open(null, "redux-devtools", "menubar=no,location=no,resizable=yes,scrollbars=no,status=no");
                win.location.reload();
                setTimeout(function() {
                    React.render(
                        <DebugPanel top right bottom left >
                            <DevTools store={scope.store} monitor={scope.monitor} />
                        </DebugPanel>
                    , win.document.body);
                }, 10);
            }

            React.render(
                <Provider store={scope.store}>
                    {() => <scope.Component {...scope.props} />}
                </Provider>,
                element[0]
            );

            scope.$on("$destroy", function() {
                React.unmountComponentAtNode(element[0]);
            });
        }
    };
}]);

MetabaseDirectives.directive('mbReactComponent', ['$timeout', function ($timeout) {
    return {
        restrict: 'A',
        link: function (scope, element, attr) {
            var Component = scope[attr.mbReactComponent];
            delete scope[attr.mbReactComponent];

            function render() {
                var props = {};
                function copyProps(dest, src) {
                    for (var key in src) {
                        copyProp(dest, key, src[key]);
                    }
                }
                function copyProp(dest, key, value) {
                    if (typeof value === "function") {
                        dest[key] = function() {
                            try {
                                return value.apply(this, arguments);
                            } finally {
                                $timeout(() => scope.$digest());
                            }
                        }
                        copyProps(dest[key], value);
                    } else {
                        dest[key] = value;
                    }
                }
                copyProps(props, scope)
                React.render(<Component {...props}/>, element[0]);
            }

            scope.$on("$destroy", function() {
                React.unmountComponentAtNode(element[0]);
            });

            // limit renders to once per animation frame
            var timeout;
            scope.$watch(function() {
                if (!timeout) {
                    timeout = requestAnimationFrame(function() {
                        timeout = null;
                        render();
                    });
                }
            });

            render();
        }
    };
}]);

var NavbarDirectives = angular.module('metabase.navbar.directives', []);

NavbarDirectives.directive('mbProfileLink', [function () {

    return {
        restrict: 'A',
        template: '

',
        controller: function ($scope) {
            $scope.ProfileLink = ProfileLink;
        },
        scope: {
            context: '=',
            user: '='
        },
    };
}]);

var MetabaseACEEditorDirectives = angular.module('metabase.aceeditor.directives', ['ui.ace']);

MetabaseACEEditorDirectives.directive('mbAceSqlEditor', function() {

    function controller($scope, Metabase) {
        $scope.aceLoaded = function(aceEditor) {
            if ($scope.onLoad) {
                var fn = $scope.onLoad();
                if (fn) fn(aceEditor);
            }

            var aceLanguageTools = ace.require('ace/ext/language_tools');
            aceEditor.setOptions({
                enableBasicAutocompletion: true,
                enableSnippets: true,
                enableLiveAutocompletion: true
            });

            aceLanguageTools.addCompleter({
                getCompletions: function(editor, session, pos, prefix, callback) {
                    if (prefix.lengh === 0 || !$scope.database) {
                        console.log("$scope.database is not set, unable to perform autocompletions for ACE Editor :'(");
                        callback(null, []);
                        return;
                    }

                    Metabase.db_autocomplete_suggestions({
                        dbId: $scope.database,
                        prefix: prefix
                    }, function(results) {
                        // transform results of the API call into what ACE expects
                        var js_results = results.map(function(result) {
                            return {
                                name: result[0],
                                value: result[0],
                                meta: result[1]
                            };
                        });
                        callback(null, js_results);

                    }, function(error) {
                        console.log(error);
                        callback(null, []);
                    });
                }
            });

            // focus the editor on load to allow faster editing of query
            aceEditor.focus();
        };
    }

    return {
        restrict: 'E',
        templateUrl: '/app/components/editor/editor.html',
        controller: ['$scope', 'Metabase', controller],
        scope: {
            sql: '=', // the text of the editor itself
            database: '=', // int ID of DB to use for autocompletion
            onLoad: '&onload' // optional callback of the form fn(aceEditor) in case we need a reference to it (e.g. so we can aceEditor.focus())
        }
    };
});
<|endoftext|>


<commit_before>/**
 * A result.
 * @prop {boolean} success Whether the command execution was successful.
 * @prop {?Command} command The executed command.
 * @prop {?string} commandName The parsed command name.
 * @prop {?CommandError} commandError The command error.
 * @prop {?string} errorReason The reason for the failed execution.
 */
class Result {
  /**
   * @typedef {object} ResultOptions The result options.
   * @prop {boolean} success Whether the command execution was successful.
   * @prop {?Command} command The executed command.
   * @prop {?string} commandName The parsed command name.
   * @prop {?CommandError} commandError The command error.
   * @prop {?string} errorReason The reason for the failed execution.
   */

  /**
   * @param {ResultOptions} options The result options.
   */
  constructor(options) {
    this.success = options.success;
    this.command = options.command;
    this.commandName = options.command !== undefined ? options.command.name : options.commandName;
    this.commandError = options.commandError;
    this.errorReason = options.errorReason;
  }
}

module.exports = Result;
<commit_msg>quick fox in the forest<commit_after>/**
 * A result.
 * @prop {boolean} success Whether the command execution was successful.
 * @prop {?Command} command The executed command.
 * @prop {?string} commandName The parsed command name.
 * @prop {?CommandError} commandError The command error.
 * @prop {?string} errorReason The reason for the failed execution.
 */
class Result {
  /**
   * @typedef {object} ResultOptions The result options.
   * @prop {boolean} success Whether the command execution was successful.
   * @prop {?Command} command The executed command.
   * @prop {?string} commandName The parsed command name.
   * @prop {?CommandError} commandError The command error.
   * @prop {?string} errorReason The reason for the failed execution.
   */

  /**
   * @param {ResultOptions} options The result options.
   */
  constructor(options) {
    this.success = options.success;
    this.command = options.command;
    this.commandName = options.command !== undefined ? options.command.names[0] : options.commandName;
    this.commandError = options.commandError;
    this.errorReason = options.errorReason;
  }
}

module.exports = Result;
<|endoftext|>


<commit_before>var _ = require('underscore');
var $ = require('jquery');

var MutationObserver = window.MutationObserver;

var MARGINS = {
  top: 8,
  bottom: 8
};

var initObserver = function (popup, onChangeMutation) {
  var config = {
    childList: true,
    subtree: true
  };

  if (!MutationObserver) {
    return;
  }

  var observer = new MutationObserver(onChangeMutation);
  observer.observe(popup.get ? popup.get(0) : popup, config);
  return observer;
};

var initScroll = function (reference) {
  return reference.closest('.js-perfect-scroll');
};

var Manager = function (id, reference, popup) {
  this.id = id;
  this.reference = reference;
  this.popup = popup;
  this.state = {
    position: 'bottom',
    scroll: 0
  };

  // to capture click events and avoid close the dialog
  popup.attr('data-cid', id);
  popup.attr('data-dialog', id);
};

Manager.prototype = {
  append: function (mode) {
    this.mode = mode;
    var popup = this.popup.get(0);
    var ref = this.reference.get(0);
    if (mode === 'float') {
      document.body.appendChild(popup);
    } else {
      ref.appendChild(popup);
    }
  },

  track: function () {
    if (this.mode !== 'float') {
      return;
    }

    // we need to watch when the dialog's height changes in order to reposition it again
    this.observer = initObserver(this.popup, this.onChangeMutation.bind(this));
    this.emitter = initScroll(this.reference);
    this.repositionBinded = _.throttle(this.reposition.bind(this), 15);

    if (this.emitter) {
      this.emitter
        .on('ps-scroll-x', this.repositionBinded)
        .on('ps-scroll-y', this.repositionBinded);
    }

    this.reposition();
  },

  onChangeMutation: function (mutations) {
    _.each(mutations, function (mutation) {
      var target = $(mutation.target);
      var id = target.attr('data-cid');

      if (!id) {
        id = target.closest('[data-cid]').attr('data-cid');
      }

      if (id === this.id) {
        this.repositionBinded();
      }
    }, this);
  },

  reposition: function () {
    if (this.mode !== 'float') {
      return;
    }

    var scroll = this.emitter.get(0).scrollTop;

    var dh = $('body').height();
    var ref = this.reference;
    var popup = this.popup;
    var state = this.state;

    var refPosition = ref.offset();

    var onBottom = refPosition.top + ref.height() + MARGINS.top;
    var onTop = refPosition.top - popup.height() - MARGINS.bottom;
    var ph = popup.height();
    var top;

    // If this condition matches, the dialog has changed its size
    if (state.scroll === scroll) {
      // We maintain position unless there is no space
      if (state.position === 'top') {
        if (onTop <= 0) {
          top = onBottom;
          state.position = 'bottom';
        } else {
          top = onTop;
        }
      } else {
        if (onBottom + ph + MARGINS.bottom >= dh) {
          top = onTop;
          state.position = 'top';
        } else {
          top = onBottom;
        }
      }
    } else {
      if (onBottom + ph + MARGINS.bottom >= dh) {
        top = onTop;
        state.position = 'top';
      } else {
        top = onBottom;
        state.position = 'bottom';
      }
    }

    // Boundries come from document height to avoid scroll
    if (top < 0) {
      top = MARGINS.top;
    } else {
      top = Math.min(top, dh - ph - MARGINS.bottom);
    }

    popup.css({
      top: top,
      left: refPosition.left
    });

    state.scroll = scroll;
  },

  untrack: function () {
    this.observer && this.observer.disconnect();
    if (this.emitter) {
      this.emitter
        .off('ps-scroll-x', this.repositionBinded)
        .off('ps-scroll-y', this.repositionBinded);
    }
  },

  destroy: function () {
    this.untrack();

    var el = document.querySelector('[data-dialog=' + this.id + ']');
    if (el && document.body.contains(el)) {
      document.body.removeChild(el);
    }

    this.emitter = null;
    this.reference = null;
    this.popup = null;
  }
};

module.exports = Manager;
<commit_msg>Improvements on popup manager.<commit_after>var _ = require('underscore');
var $ = require('jquery');

var MutationObserver = window.MutationObserver;

var MARGINS = {
  top: 8,
  bottom: 8
};

var initObserver = function (popup, onChangeMutation) {
  var config = {
    childList: true,
    subtree: true
  };

  if (!MutationObserver) {
    return;
  }

  var observer = new MutationObserver(onChangeMutation);
  observer.observe(popup.get ? popup.get(0) : popup, config);
  return observer;
};

var initScroll = function (reference) {
  return reference.closest('.js-perfect-scroll');
};

var Manager = function (id, reference, popup) {
  this.id = id;
  this.reference = reference;
  this.popup = popup;
  this.state = {
    position: 'bottom',
    scroll: 0
  };

  // to capture click events and avoid close the dialog
  popup.attr('data-cid', id);
  popup.attr('data-dialog', id);
};

Manager.prototype = {
  append: function (mode) {
    this.mode = mode;
    var popup = this.popup.get(0);
    var ref = this.reference.get(0);
    if (mode === 'float') {
      document.body.appendChild(popup);
    } else {
      ref.appendChild(popup);
    }
  },

  track: function () {
    if (this.mode !== 'float') {
      return;
    }

    // we need to watch when the dialog's height changes in order to reposition it again
    this.observer = initObserver(this.popup, this.onChangeMutation.bind(this));
    this.emitter = initScroll(this.reference);
    this.repositionBinded = _.throttle(this.reposition.bind(this), 15);

    if (this.emitter) {
      this.emitter
        .on('ps-scroll-x', this.repositionBinded)
        .on('ps-scroll-y', this.repositionBinded);
    }

    this.reposition();
  },

  onChangeMutation: function (mutations) {
    _.each(mutations, function (mutation) {
      var target = $(mutation.target);
      var id = target.attr('data-cid');

      if (!id) {
        id = target.closest('[data-cid]').attr('data-cid');
      }

      if (id === this.id) {
        this.repositionBinded();
      }
    }, this);
  },

  reposition: function () {
    if (this.mode !== 'float' || !this.emitter) {
      return;
    }

    var scroll = this.emitter.get(0).scrollTop;

    var dh = $(window).height();
    var ref = this.reference;
    var popup = this.popup;
    var state = this.state;

    var refPosition = ref.offset();
    var ph = popup.outerHeight();

    var onBottom = refPosition.top + ref.outerHeight() + MARGINS.top;
    var onTop = refPosition.top - ph - MARGINS.bottom;
    var top;

    // If this condition matches, the dialog has changed its size
    if (state.scroll === scroll) {
      // We maintain position unless there is no space
      if (state.position === 'top') {
        if (onTop <= 0) {
          top = onBottom;
          state.position = 'bottom';
        } else {
          top = onTop;
        }
      } else {
        if (onBottom + ph + MARGINS.bottom >= dh) {
          top = onTop;
          state.position = 'top';
        } else {
          top = onBottom;
        }
      }
    } else {
      if (onBottom + ph + MARGINS.bottom >= dh) {
        top = onTop;
        state.position = 'top';
      } else {
        top = onBottom;
        state.position = 'bottom';
      }
    }

    // Boundries come from document height to avoid scroll
    if (top < 0) {
      top = MARGINS.top;
    } else {
      top = Math.min(top, dh - ph - MARGINS.bottom);
    }

    popup.css({
      top: top,
      left: refPosition.left
    });

    state.scroll = scroll;
  },

  untrack: function () {
    this.observer && this.observer.disconnect();
    if (this.emitter) {
      this.emitter
        .off('ps-scroll-x', this.repositionBinded)
        .off('ps-scroll-y', this.repositionBinded);
    }
  },

  destroy: function () {
    this.untrack();

    var el = document.querySelector('[data-dialog=' + this.id + ']');
    if (el) {
      el.parentNode.removeChild(el);
    }

    this.emitter = null;
    this.reference = null;
    this.popup = null;
  }
};

module.exports = Manager;
<|endoftext|>


<commit_before>import R from 'ramda';
import { createSelector } from 'reselect';
import { getNodes } from './node';
import { getNodeTypes, getPinsByNodeTypeId } from './nodetype';
import * as PIN_DIRECTION from '../constants/pinDirection';
import * as PIN_VALIDITY from '../constants/pinValidity';

export const getPins = (state) => R.pipe(
  R.prop('pins')
)(state);

export const getPinsByNodeId = (state, props) => R.pipe(
  getPins,
  R.filter((pin) => pin.nodeId === props.id)
)(state, props);

export const getPinsByIds = (state, props) => R.pipe(
  getPins,
  R.values,
  R.reduce((p, pin) => {
    let result = p;
    if (props && props.pins && props.pins.indexOf(pin.id) !== -1) {
      result = R.assic(pin.id, pin, p);
    }
    return result;
  }, {})
)(state, props);

export const getFullPinsData = createSelector(
  [getPins, getNodeTypes, getNodes],
  (pins, nodeTypes, nodes) => R.pipe(
    R.values,
    R.reduce((p, pin) => {
      const node = nodes[pin.nodeId];
      const nodeTypePins = getPinsByNodeTypeId({ nodeTypes }, node.typeId);
      const originalPin = nodeTypePins[pin.key];

      return R.assoc(pin.id, R.merge(pin, originalPin), p);
    }, {})
  )(pins)
);

export const getValidPins = (pins, links, forPinId) => {
  const oPin = pins[forPinId];
  return R.pipe(
    R.values,
    R.reduce((p, pin) => {
      const samePin = (pin.id === oPin.id);
      const sameNode = (pin.nodeId === oPin.nodeId);
      const sameDirection = (pin.direction === oPin.direction);
      const sameType = (pin.type === oPin.type);
      const pinLinks = R.pipe(
        R.values,
        R.filter((link) => (link.fromPinId === pin.id || link.toPinId === pin.id))
      )(links);
      const canHaveLink = (
        (
          pin.direction === PIN_DIRECTION.INPUT &&
          pinLinks.length === 0
        ) ||
        pin.direction === PIN_DIRECTION.OUTPUT
      );

      let validness = PIN_VALIDITY.INVALID;

      if (!samePin && !sameNode && canHaveLink) {
        if (!sameDirection) { validness = PIN_VALIDITY.ALMOST; }
        if (!sameDirection && sameType) { validness = PIN_VALIDITY.VALID; }
      }

      const result = {
        id: pin.id,
        validness,
      };

      return R.assoc(pin.id, result, p);
    }, {})
  )(pins);
};
<commit_msg>refactor(pinSelector): simplified getPins method<commit_after>import R from 'ramda';
import { createSelector } from 'reselect';
import { getNodes } from './node';
import { getNodeTypes, getPinsByNodeTypeId } from './nodetype';
import * as PIN_DIRECTION from '../constants/pinDirection';
import * as PIN_VALIDITY from '../constants/pinValidity';

export const getPins = R.prop('pins');

export const getPinsByNodeId = (state, props) => R.pipe(
  getPins,
  R.filter((pin) => pin.nodeId === props.id)
)(state, props);

export const getPinsByIds = (state, props) => R.pipe(
  getPins,
  R.values,
  R.reduce((p, pin) => {
    let result = p;
    if (props && props.pins && props.pins.indexOf(pin.id) !== -1) {
      result = R.assic(pin.id, pin, p);
    }
    return result;
  }, {})
)(state, props);

export const getFullPinsData = createSelector(
  [getPins, getNodeTypes, getNodes],
  (pins, nodeTypes, nodes) => R.pipe(
    R.values,
    R.reduce((p, pin) => {
      const node = nodes[pin.nodeId];
      const nodeTypePins = getPinsByNodeTypeId({ nodeTypes }, node.typeId);
      const originalPin = nodeTypePins[pin.key];

      return R.assoc(pin.id, R.merge(pin, originalPin), p);
    }, {})
  )(pins)
);

export const getValidPins = (pins, links, forPinId) => {
  const oPin = pins[forPinId];
  return R.pipe(
    R.values,
    R.reduce((p, pin) => {
      const samePin = (pin.id === oPin.id);
      const sameNode = (pin.nodeId === oPin.nodeId);
      const sameDirection = (pin.direction === oPin.direction);
      const sameType = (pin.type === oPin.type);
      const pinLinks = R.pipe(
        R.values,
        R.filter((link) => (link.fromPinId === pin.id || link.toPinId === pin.id))
      )(links);
      const canHaveLink = (
        (
          pin.direction === PIN_DIRECTION.INPUT &&
          pinLinks.length === 0
        ) ||
        pin.direction === PIN_DIRECTION.OUTPUT
      );

      let validness = PIN_VALIDITY.INVALID;

      if (!samePin && !sameNode && canHaveLink) {
        if (!sameDirection) { validness = PIN_VALIDITY.ALMOST; }
        if (!sameDirection && sameType) { validness = PIN_VALIDITY.VALID; }
      }

      const result = {
        id: pin.id,
        validness,
      };

      return R.assoc(pin.id, result, p);
    }, {})
  )(pins);
};
<|endoftext|>


<commit_before>$(document).ready(function(){
	$(".fancybox").fancybox({
		'scrolling'		: 'auto',
		'width'			: '8',
		'height'		: '10',
		'titleShow'		: false,
		'autoScale'		: true,
		'type'			: 'iframe'
	});

	var datas = new Array(); // All datas
	var temp_id = null; // temp_id pour edition
	var temp  = new Array(); // temp data
	var increment = 0; // ID unique pour les pub

	/** Limited DATE **/
	if( $("#limited").attr('checked') )
		$("#limited_date").show();
	else
		$("#limited_date").hide();

	$("#limited").on("change", function(){
		var temp_check = $(this).attr('checked');

		if( temp_check )
			$("#limited_date").show();
		else
			$("#limited_date").hide();
	});

	jsonLoad(); // Load un json
	/** Fonction qui load le contenue d'un json **/
	function jsonLoad()
	{
		if(datasPubEdit) // Si le json existe (voir .tpl)
		{
			datas = jQuery.parseJSON($("#datas").val());
			var i = 0;
			$("#pub_manage img.pubs_pub").each(function(){ // Boucle sur toutes les miniatures
				i++;
			});
			increment = i;

			/** Appel des functions **/
			generateJson();
		}
	}

	/** Définit une taille max pour l'aperçu de l'image upload **/
	$("#image_preview").css("max-width", "120px");

	/** Type de page **/
	$("#link_type-0").attr('checked', true);
	showLinkType(0);

	function showLinkType(i)
	{
		if(i == 1)
		{
			$("#page_link_internal").hide();
			$("#page_link_external").show();
		}
		else if (i == 0)
		{
			$("#page_link_internal").show();
			$("#page_link_external").hide();
		}
	}

	$("#link_type-0").change(function(){
		showLinkType(0);
	});
	$("#link_type-1").change(function(){
		showLinkType(1);
	});

	/** Ajouter du texte sur une image **/
	$("#addtext").attr('checked', false); // Par défaut, checkbox not checked
	$("#addtext_text").hide();			  // On cache le tinymce

	$("#addtext").on("change", function(){
		var temp_check = $(this).attr('checked');
		if( temp_check )
		{
			$("#addtext_text").show();
			tinyMCE.execCommand('mceAddControl', false, "text");
		}
		else
		{
			tinyMCE.execCommand('mceRemoveControl', false, "text");
			$("#addtext_text").hide();
		}
	});

	/** Suppresion de toutes les pubs **/
	$('#pub_del').click(function(e) {
		e.preventDefault(); // Permet de ne pas revenir en haut de page
		datas = new Array();
		$('#pub_manage div.pub').each(function(){
			$(this).remove();
		});
		generateJson();
	});

	/** Suppresion d'une pub **/
	$("a.pub_delete").on("click", function(e) {
		e.preventDefault(); // Permet de ne pas revenir en haut de page
		var selector_pub = $(this).siblings("img.pubs_pub");
		var id = $(selector_pub).attr("rel");

		delete datas[id];

		$(this).parent().remove();
		generateJson(); // Génération du nouveau Json
	});

	/** Génération du tableau json **/
	function generateJson()
	{
		$("#datas").attr("value", json_encode(datas));
	}

	/** Fonction de nétoyage du formulaire pour editer une pub **/
	function clearFormEdit()
	{
		$("#image").val("");
		imageCancelImage();
		$("#window").attr('checked', false);
		$("#external_page").val("");
		$("#link_type-0").attr('checked', true);
		$("select option:selected").attr("selected", "");
		showLinkType(0);
		$("#addtext").attr('checked', false);
		tinyMCE.execCommand('mceRemoveControl', false, "text");
		$("#text").val("");
		$("#addtext_text").hide();
		$("#weight").val("");
	}

	/** Close fancybox **/
	$("#cancel_pub").on("click", function(){
		$.fancybox.close();
		return false;
	});

	/** Event lors de l'envoit d'une image **/
	$("#image").bind("image_set", function(e, params){
		if( params.custom )
		{
			temp["path_thumb"] = params.custom.thumbnail_url;
		    temp["height"] = params.custom.height;
			temp["width"] = params.custom.width;
			temp["theight"] = params.custom.theight;
			temp["twidth"] = params.custom.twidth;
		}
	});

	/** Ajout ou modifciation d'une pub **/
	$("#pub_add, #pub_manage .pubs_pub").on("click", function(e){
		if ( $(this).attr("rel") ) // Vrai si edition d'une image
			temp_id = $(this).attr("rel"); // Récupération de l'id à éditer

		if( temp_id != null) // Edition, on charge les données dans le formulaire
		{
			$("#image").attr("value", datas[temp_id]["image_path"]);
			$("#image_preview").attr("src", datas[temp_id]["image_path_thumb"]);
			$("#imageSelectImg").css("display", "inline");
			if(datas[temp_id]["external_page"] != "")
				$("#external_page").attr("value", datas[temp_id]["external_page"]);
			if( datas[temp_id]["page_link"] != "" )
			{
				$("option").each(function(){
					if($(this).val() == datas[temp_id]["page_link"])
						$(this).attr("selected", "selected");
				})
			}
			if( ( datas[temp_id]["link_type"] == 1 ) && ( datas[temp_id]["link_type"] != "" ) )
			{
				$("#link_type-1").attr('checked', true);
				$("#link_type-0").attr('checked', false);
				showLinkType(1);
			}
			else
			{
				$("#link_type-1").attr('checked', false);
				$("#link_type-0").attr('checked', true);
			}
			if( (datas[temp_id]["window"]) && (datas[temp_id]["window"] != "") )
				$("#window").attr('checked', true);
			else
				$("#window").attr('checked', false);
			$("#weight").attr("value", datas[temp_id]["weight"]);

			if( datas[temp_id]["addtext"] && (datas[temp_id]["addtext"] != "") )
			{
				$("#addtext").attr('checked', true);
				$("#addtext_text").show();
			}
			else
			{
				$("#addtext").attr('checked', false);
				$("#addtext_text").hide();
			}
		}

		// Configuration de la fancybox
		$.fancybox({
			'width'			: 760,
			'height'		: 600,
			'autoScale'		: false,
			'autoDimensions': false,
			'scrolling'		: 'yes',
			'titleShow'		: false,
			'onComplete'	: function(){
				$('form').validationEngine('hide');
				if( temp_id != null) { if( datas[temp_id]["addtext"]) {  tinyMCE.execCommand('mceAddControl', false, "text"); $('#text').val(datas[temp_id]["text"]); } };
			},
			'onCleanup' : function(){
				$('#formAdvert').validationEngine('hide');
				$("#valid_image").unbind();
				$("#cancel_image").unbind();
				temp_id = null; // ID d'édition remis à null
				clearFormEdit(); // Appel de la fonction permettant de vider les champs du formulaire
				temp = new Array(); // Vide le tableau temporaire
			},
			'type'			: 'inline',
			'href' 			: '#edit_pub'
		});
		return false;
	});

	/** Valid form in fancybox **/
	$("#valid_pub").on("click", function(){

		if ( $("#formAdvert").validationEngine('validate') )
		{
			if( temp_id != null )
			{
				var id = temp_id; // ID de l'édition
			}
			else
			{
				increment++
				var id = increment; // Nouvel id unique
				datas[id] = new Array();
			}

			// Enregistrement des données
			if( datas[id]["image_path"] != $("#image").val())
			{
				datas[id]["image_path"] 			= $("#image").val();
				datas[id]["image_path_thumb"] 		= temp["path_thumb"];
				datas[id]["image_width"] 			= temp["width"];
				datas[id]["image_height"] 			= temp["height"];
				datas[id]["image_thumb_width"] 		= temp["twidth"];
				datas[id]["image_thumb_height"] 	= temp["theight"];
			}
			datas[id]["window"]			= ($("#window").attr("checked") != null) ? $("#window").attr("checked") : "";
			datas[id]["link_type"] 		= ($("input[name='link_type']:checked").val() != null) ? $("input[name='link_type']:checked").val() : "";
			datas[id]["external_page"] 	= ($("#external_page").val() != null) ? $("#external_page").val() : "";
			datas[id]["page_link"] 		= ($("select option:selected").val() != null) ? $("select option:selected").val() : "";
			datas[id]["weight"]			= ($("#weight").val() != null) ? $("#weight").val() : "";
			datas[id]["addtext"] 		= ($("#addtext").attr("checked") != null) ? $("#addtext").attr("checked") : "";

			if( datas[id]["addtext"] && (datas[id]["addtext"] != "") )
				datas[id]["text"] = tinyMCE.activeEditor.getContent();
			else
				datas[id]["text"] = "";

			if( temp_id == null )
			{
				// Ajout de la nouvelle pub dans le html
				$("#pub_manage").append("

");
			}

			generateJson();
			$.fancybox.close();
		}

		return false;
	});

	/** Fonction permettant de générer un tableau JSON. /!\ Trouvé sur internet ! **/
	/* http://www.perkiset.org/forum/javascript/json_encoder_for_javascript-t1094.0.html */
	function json_encode(inVal) { return _json_encode(inVal).join(''); }
	function _json_encode(inVal, out)
	  {
	    out = out || new Array();
	    var undef; // undefined
	    switch (typeof inVal)
	      {
	        case 'object':
	          if (!inVal)
	            {
	              out.push('null');
	            }
	          else
	            {
	              if (inVal.constructor == Array)
	                {
	                  // Need to make a decision... if theres any associative
	                  // elements of the array then I will block the whole
	                  // thing as an object {} otherwise, I'll block it
	                  // as a  normal array []
	                  var testVal = inVal.length;
	                  var compVal = 0;
	                  for (var key in inVal)
	                    {
	                      if (inVal.hasOwnProperty(key))
	                        {
	                          compVal++;
	                        }
	                    }
	                  if (testVal != compVal)
	                    {
	                      // Associative
	                      out.push('{');
	                      i = 0;
	                      for (var key in inVal)
	                        {
	                          if (inVal.hasOwnProperty(key))
	                            {
	                              if (i++ > 0) out.push(',\n');
	                              out.push('"');
	                              out.push(key);
	                              out.push('":');
	                              _json_encode(inVal[key], out);
	                            }
	                        }
	                      out.push('}');
	                    }
	                  else
	                    {
	                      // Standard array...
	                      out.push('[');
	                      for (var i = 0; i < inVal.length; ++i)
	                        {
	                          if (inVal.hasOwnProperty(i))
	                            {
	                              if (i > 0) out.push(',\n');
	                              _json_encode(inVal[i], out);
	                            }
	                        }
	                      out.push(']');
	                    }

	                }
	              else
	                {
	                  if (typeof inVal.toString != 'undefined')
	                    {
	                      out.push('{');
	                      var first = true;
	                      for (var i in inVal)
	                        {
	                          if (inVal.hasOwnProperty(i))
	                            {
	                              var curr = out.length; // Record position to allow undo when arg[i] is undefined.
	                              if (!first) out.push(',\n');
	                              _json_encode(i, out);
	                              out.push(':');
	                              _json_encode(inVal[i], out);
	                              if (out[out.length - 1] == undef)
	                                {
	                                  out.splice(curr, out.length - curr);
	                                }
	                              else
	                                {
	                                  first = false;
	                                }
	                            }
	                        }
	                      out.push('}');
	                    }
	                }
	            }
	          return out;

	        case 'unknown':
	        case 'undefined':
	        case 'function':
	          out.push(undef);
	          return out;

	        case 'string':
	          out.push('"');
	          out.push(inVal.replace(/(["\\])/g, '\$1').replace(/\r/g, '').replace(/\n/g, '\n'));
	          out.push('"');
	          return out;

	        default:
	          out.push(String(inVal));
	          return out;
	      }
	  }

});<commit_msg>Fix module advert<commit_after>$(document).ready(function(){
	$(".fancybox").fancybox({
		'scrolling'		: 'auto',
		'width'			: '8',
		'height'		: '10',
		'titleShow'		: false,
		'autoScale'		: true,
		'type'			: 'iframe'
	});

	var datas = new Array(); // All datas
	var temp_id = null; // temp_id pour edition
	var temp  = new Array(); // temp data
	var increment = 0; // ID unique pour les pub

	/** Limited DATE **/
	if( $("#limited").attr('checked') )
		$("#limited_date").show();
	else
		$("#limited_date").hide();

	$("#limited").on("change", function(){
		var temp_check = $(this).attr('checked');

		if( temp_check )
			$("#limited_date").show();
		else
			$("#limited_date").hide();
	});

	jsonLoad(); // Load un json
	/** Fonction qui load le contenue d'un json **/
	function jsonLoad()
	{
		if(typeof datasPubEdit !== "undefined") // Si le json existe (voir .tpl)
		{
			datas = jQuery.parseJSON($("#datas").val());
			var i = 0;
			$("#pub_manage img.pubs_pub").each(function(){ // Boucle sur toutes les miniatures
				i++;
			});
			increment = i;

			/** Appel des functions **/
			generateJson();
		}
	}

	/** Définit une taille max pour l'aperçu de l'image upload **/
	$("#image_preview").css("max-width", "120px");

	/** Type de page **/
	$("#link_type-0").attr('checked', true);
	showLinkType(0);

	function showLinkType(i)
	{
		if(i == 1)
		{
			$("#page_link_internal").hide();
			$("#page_link_external").show();
		}
		else if (i == 0)
		{
			$("#page_link_internal").show();
			$("#page_link_external").hide();
		}
	}

	$("#link_type-0").change(function(){
		showLinkType(0);
	});
	$("#link_type-1").change(function(){
		showLinkType(1);
	});

	/** Ajouter du texte sur une image **/
	$("#addtext").attr('checked', false); // Par défaut, checkbox not checked
	$("#addtext_text").hide();			  // On cache le tinymce

	$("#addtext").on("change", function(){
		var temp_check = $(this).attr('checked');
		if( temp_check )
		{
			$("#addtext_text").show();
			tinyMCE.execCommand('mceAddControl', false, "text");
		}
		else
		{
			tinyMCE.execCommand('mceRemoveControl', false, "text");
			$("#addtext_text").hide();
		}
	});

	/** Suppresion de toutes les pubs **/
	$('#pub_del').click(function(e) {
		e.preventDefault(); // Permet de ne pas revenir en haut de page
		datas = new Array();
		$('#pub_manage div.pub').each(function(){
			$(this).remove();
		});
		generateJson();
	});

	/** Suppresion d'une pub **/
	$("a.pub_delete").on("click", function(e) {
		e.preventDefault(); // Permet de ne pas revenir en haut de page
		var selector_pub = $(this).siblings("img.pubs_pub");
		var id = $(selector_pub).attr("rel");

		delete datas[id];

		$(this).parent().remove();
		generateJson(); // Génération du nouveau Json
	});

	/** Génération du tableau json **/
	function generateJson()
	{
		$("#datas").attr("value", json_encode(datas));
	}

	/** Fonction de nétoyage du formulaire pour editer une pub **/
	function clearFormEdit()
	{
		$("#image").val("");
		imageCancelImage();
		$("#window").attr('checked', false);
		$("#external_page").val("");
		$("#link_type-0").attr('checked', true);
		$("select option:selected").attr("selected", "");
		showLinkType(0);
		$("#addtext").attr('checked', false);
		tinyMCE.execCommand('mceRemoveControl', false, "text");
		$("#text").val("");
		$("#addtext_text").hide();
		$("#weight").val("");
	}

	/** Close fancybox **/
	$("#cancel_pub").on("click", function(){
		$.fancybox.close();
		return false;
	});

	/** Event lors de l'envoit d'une image **/
	$("#image").bind("image_set", function(e, params){
		if( params.custom )
		{
			temp["path_thumb"] = params.custom.thumbnail_url;
		    temp["height"] = params.custom.height;
			temp["width"] = params.custom.width;
			temp["theight"] = params.custom.theight;
			temp["twidth"] = params.custom.twidth;
		}
	});

	/** Ajout ou modifciation d'une pub **/
	$("#pub_add, #pub_manage .pubs_pub").on("click", function(e){
		if ( $(this).attr("rel") ) // Vrai si edition d'une image
			temp_id = $(this).attr("rel"); // Récupération de l'id à éditer

		if( temp_id != null) // Edition, on charge les données dans le formulaire
		{
			$("#image").attr("value", datas[temp_id]["image_path"]);
			$("#image_preview").attr("src", datas[temp_id]["image_path_thumb"]);
			$("#imageSelectImg").css("display", "inline");
			if(datas[temp_id]["external_page"] != "")
				$("#external_page").attr("value", datas[temp_id]["external_page"]);
			if( datas[temp_id]["page_link"] != "" )
			{
				$("option").each(function(){
					if($(this).val() == datas[temp_id]["page_link"])
						$(this).attr("selected", "selected");
				})
			}
			if( ( datas[temp_id]["link_type"] == 1 ) && ( datas[temp_id]["link_type"] != "" ) )
			{
				$("#link_type-1").attr('checked', true);
				$("#link_type-0").attr('checked', false);
				showLinkType(1);
			}
			else
			{
				$("#link_type-1").attr('checked', false);
				$("#link_type-0").attr('checked', true);
			}
			if( (datas[temp_id]["window"]) && (datas[temp_id]["window"] != "") )
				$("#window").attr('checked', true);
			else
				$("#window").attr('checked', false);
			$("#weight").attr("value", datas[temp_id]["weight"]);

			if( datas[temp_id]["addtext"] && (datas[temp_id]["addtext"] != "") )
			{
				$("#addtext").attr('checked', true);
				$("#addtext_text").show();
			}
			else
			{
				$("#addtext").attr('checked', false);
				$("#addtext_text").hide();
			}
		}

		// Configuration de la fancybox
		$.fancybox({
			'width'			: 760,
			'height'		: 600,
			'autoScale'		: false,
			'autoDimensions': false,
			'scrolling'		: 'yes',
			'titleShow'		: false,
			'onComplete'	: function(){
				$('form').validationEngine('hide');
				if( temp_id != null) { if( datas[temp_id]["addtext"]) {  tinyMCE.execCommand('mceAddControl', false, "text"); $('#text').val(datas[temp_id]["text"]); } };
			},
			'onCleanup' : function(){
				$('#formAdvert').validationEngine('hide');
				$("#valid_image").unbind();
				$("#cancel_image").unbind();
				temp_id = null; // ID d'édition remis à null
				clearFormEdit(); // Appel de la fonction permettant de vider les champs du formulaire
				temp = new Array(); // Vide le tableau temporaire
			},
			'type'			: 'inline',
			'href' 			: '#edit_pub'
		});
		return false;
	});

	/** Valid form in fancybox **/
	$("#valid_pub").on("click", function(){

		if ( $("#formAdvert").validationEngine('validate') )
		{
			if( temp_id != null )
			{
				var id = temp_id; // ID de l'édition
			}
			else
			{
				increment++
				var id = increment; // Nouvel id unique
				datas[id] = new Array();
			}

			// Enregistrement des données
			if( datas[id]["image_path"] != $("#image").val())
			{
				datas[id]["image_path"] 			= $("#image").val();
				datas[id]["image_path_thumb"] 		= temp["path_thumb"];
				datas[id]["image_width"] 			= temp["width"];
				datas[id]["image_height"] 			= temp["height"];
				datas[id]["image_thumb_width"] 		= temp["twidth"];
				datas[id]["image_thumb_height"] 	= temp["theight"];
			}
			datas[id]["window"]			= ($("#window").attr("checked") != null) ? $("#window").attr("checked") : "";
			datas[id]["link_type"] 		= ($("input[name='link_type']:checked").val() != null) ? $("input[name='link_type']:checked").val() : "";
			datas[id]["external_page"] 	= ($("#external_page").val() != null) ? $("#external_page").val() : "";
			datas[id]["page_link"] 		= ($("select option:selected").val() != null) ? $("select option:selected").val() : "";
			datas[id]["weight"]			= ($("#weight").val() != null) ? $("#weight").val() : "";
			datas[id]["addtext"] 		= ($("#addtext").attr("checked") != null) ? $("#addtext").attr("checked") : "";

			if( datas[id]["addtext"] && (datas[id]["addtext"] != "") )
				datas[id]["text"] = tinyMCE.activeEditor.getContent();
			else
				datas[id]["text"] = "";

			if( temp_id == null )
			{
				// Ajout de la nouvelle pub dans le html
				$("#pub_manage").append("

");
			}

			generateJson();
			$.fancybox.close();
		}

		return false;
	});

	/** Fonction permettant de générer un tableau JSON. /!\ Trouvé sur internet ! **/
	/* http://www.perkiset.org/forum/javascript/json_encoder_for_javascript-t1094.0.html */
	function json_encode(inVal) { return _json_encode(inVal).join(''); }
	function _json_encode(inVal, out)
	  {
	    out = out || new Array();
	    var undef; // undefined
	    switch (typeof inVal)
	      {
	        case 'object':
	          if (!inVal)
	            {
	              out.push('null');
	            }
	          else
	            {
	              if (inVal.constructor == Array)
	                {
	                  // Need to make a decision... if theres any associative
	                  // elements of the array then I will block the whole
	                  // thing as an object {} otherwise, I'll block it
	                  // as a  normal array []
	                  var testVal = inVal.length;
	                  var compVal = 0;
	                  for (var key in inVal)
	                    {
	                      if (inVal.hasOwnProperty(key))
	                        {
	                          compVal++;
	                        }
	                    }
	                  if (testVal != compVal)
	                    {
	                      // Associative
	                      out.push('{');
	                      i = 0;
	                      for (var key in inVal)
	                        {
	                          if (inVal.hasOwnProperty(key))
	                            {
	                              if (i++ > 0) out.push(',\n');
	                              out.push('"');
	                              out.push(key);
	                              out.push('":');
	                              _json_encode(inVal[key], out);
	                            }
	                        }
	                      out.push('}');
	                    }
	                  else
	                    {
	                      // Standard array...
	                      out.push('[');
	                      for (var i = 0; i < inVal.length; ++i)
	                        {
	                          if (inVal.hasOwnProperty(i))
	                            {
	                              if (i > 0) out.push(',\n');
	                              _json_encode(inVal[i], out);
	                            }
	                        }
	                      out.push(']');
	                    }

	                }
	              else
	                {
	                  if (typeof inVal.toString != 'undefined')
	                    {
	                      out.push('{');
	                      var first = true;
	                      for (var i in inVal)
	                        {
	                          if (inVal.hasOwnProperty(i))
	                            {
	                              var curr = out.length; // Record position to allow undo when arg[i] is undefined.
	                              if (!first) out.push(',\n');
	                              _json_encode(i, out);
	                              out.push(':');
	                              _json_encode(inVal[i], out);
	                              if (out[out.length - 1] == undef)
	                                {
	                                  out.splice(curr, out.length - curr);
	                                }
	                              else
	                                {
	                                  first = false;
	                                }
	                            }
	                        }
	                      out.push('}');
	                    }
	                }
	            }
	          return out;

	        case 'unknown':
	        case 'undefined':
	        case 'function':
	          out.push(undef);
	          return out;

	        case 'string':
	          out.push('"');
	          out.push(inVal.replace(/(["\\])/g, '\$1').replace(/\r/g, '').replace(/\n/g, '\n'));
	          out.push('"');
	          return out;

	        default:
	          out.push(String(inVal));
	          return out;
	      }
	  }

});<|endoftext|>


<commit_before>import Ember from 'ember';
import {
    raw
    }
    from 'ic-ajax';

var config = {
    defaultViewBoxSize: 24
};

var urlRegex = /[-a-zA-Z0-9@:%_\+.~#?&//=]{2,256}\.[a-z]{2,4}\b(\/[-a-zA-Z0-9@:%_\+.~#?&//=]*)?/i;

function Icon(el, config) {
    if (el.tagName != 'svg') {
        el = Ember.$('<svg xmlns="http://www.w3.org/2000/svg">').append(el)[0];
    }

    el = $(el);

    // inject the namespace if not available...
    if (!el.attr('xmlns')) {
        el.attr('xmlns', "http://www.w3.org/2000/svg");
    }

    this.element = el;
    this.config = config;
    this.prepare();
}

Icon.prototype = {
    clone: cloneSVG,
    prepare: prepareAndStyle
};

/**
 * Clone the Icon DOM element
 */
function cloneSVG() {
    return Ember.$(this.element[0].cloneNode(true));
}

/**
 * Prepare the DOM element that will be cached in the
 * loaded iconCache store.
 */
function prepareAndStyle() {
    var viewBoxSize = this.config ? this.config.viewBoxSize : config.defaultViewBoxSize;
    var svg = this.element;

    svg[0].setAttribute('fit', '');
    svg[0].setAttribute('height', '100%');
    svg[0].setAttribute('width', '100%');
    svg[0].setAttribute('preserveAspectRatio', 'xMidYMid meet');

    svg[0].setAttribute('viewBox', svg[0].getAttribute('viewBox') || ('0 0 ' + viewBoxSize + ' ' + viewBoxSize));

    svg.css({
        'pointer-events': 'none',
        'display': 'block'
    });

    this.element = svg;
}

var IconService = Ember.Service.extend({

    iconCache: {},
    templateCache: {},

    preloadIcons: function () {

        var self = this;

        var svgRegistry = [
            {
                id: 'md-tabs-arrow',
                url: 'md-tabs-arrow.svg',
                svg: '<svg version="1.1" x="0px" y="0px" viewBox="0 0 24 24"><g><polygon points="15.4,7.4 14,6 8,12 14,18 15.4,16.6 10.8,12 "/></g></svg>'

            },
            {
                id: 'md-close',
                url: 'md-close.svg',
                svg: '<svg version="1.1" x="0px" y="0px" viewBox="0 0 24 24"><g><path d="M19 6.41l-1.41-1.41-5.59 5.59-5.59-5.59-1.41 1.41 5.59 5.59-5.59 5.59 1.41 1.41 5.59-5.59 5.59 5.59 1.41-1.41-5.59-5.59z"/></g></svg>'

            },
            {
                id: 'md-cancel',
                url: 'md-cancel.svg',
                svg: '<svg version="1.1" x="0px" y="0px" viewBox="0 0 24 24"><g><path d="M12 2c-5.53 0-10 4.47-10 10s4.47 10 10 10 10-4.47 10-10-4.47-10-10-10zm5 13.59l-1.41 1.41-3.59-3.59-3.59 3.59-1.41-1.41 3.59-3.59-3.59-3.59 1.41-1.41 3.59 3.59 3.59-3.59 1.41 1.41-3.59 3.59 3.59 3.59z"/></g></svg>'

            },
            {
                id: 'md-toggle-arrow',
                url: 'md-toggle-arrow-svg',
                svg: '<svg version="1.1" x="0px" y="0px" viewBox="0 0 48 48"><path d="M24 16l-12 12 2.83 2.83 9.17-9.17 9.17 9.17 2.83-2.83z"/><path d="M0 0h48v48h-48z" fill="none"/></svg>'

            }
        ];

        svgRegistry.forEach(function (asset) {
            //self.icon(asset.id, asset.url);
            self.templateCache[asset.url] = asset.svg;
        });

    },

    init: function () {
        this._super(...arguments
        )
        ;
        this.preloadIcons();
    },

    getIcon: function (id) {
        id = id || '';

        var self = this;

        // if already loaded and cached, use a clone of the cached icon.
        if (this.iconCache[id]) {
            return Ember.RSVP.Promise.resolve(this.iconCache[id].clone());
        }

        if (urlRegex.test(id)) {
            return this.loadByURL(id)
                .then(function (icon) {
                    return self.cacheIcon(icon, id);
                });
        }

        if (id.indexOf(':') == -1) {
            id = '$default:' + id;
        }

        return this.loadByID(id).then(function(icon){
            return self.cacheIcon(icon, id);

        // error -> try load from set
        },function(err){
            return self.loadFromIconSet(id).then(function(icon){
                return self.cacheIcon(icon, id);
            }, function(err){
                //self.announceIdNotFound(id);
            })
        });

    },

    icon: function (id, url, viewBoxSize) {
        if (id.indexOf(':') == -1) {
            id = '$default:' + id;
        }

        config[id] = {
            url: url,
            viewBoxSize: viewBoxSize || config.defaultViewBoxSize
        };
    },

    iconSet: function (id, url, viewBoxSize) {
        config[id] = {
            url: url,
            viewBoxSize: viewBoxSize || config.defaultViewBoxSize
        };
    },

    defaultIconSet: function (url, viewBoxSize) {
        var setName = '$default';

        if (!config[setName]) {
            config[setName] = {
                url: url,
                viewBoxSize: viewBoxSize || config.defaultViewBoxSize
            };
        }
    },

    defaultViewBoxSize: function (viewBoxSize) {
        config.defaultViewBoxSize = viewBoxSize;
    },

    loadByID: function (id) {
        var iconConfig = config[id];

        return !iconConfig ? Ember.RSVP.Promise.reject(id) : this.loadByURL(iconConfig.url).then(function (icon) {
            return new Icon(icon, iconConfig);
        });
    },

    loadFromIconSet: function (id) {

        var self = this;
        var setName = id.substring(0, id.lastIndexOf(':')) || '$default';
        var iconSetConfig = config[setName];

        function extractFromSet(svg) {

            if (!self.templateCache[iconSetConfig.url]) {
                self.templateCache[iconSetConfig.url] = svg;
            }

            var iconName = id.slice(id.lastIndexOf(':') + 1);
            var icon = svg.querySelector('#' + iconName);
            return !icon ? Ember.RSVP.Promise.reject(id) : new Icon(icon, iconSetConfig);
        }

        return !iconSetConfig ? Ember.RSVP.Promise.reject(id) : this.loadByURL(iconSetConfig.url).then(extractFromSet);
    },

    loadByURL: function (url) {

        // first check templateCache

        var req;

        if (this.templateCache[url]) {
            req = Ember.RSVP.Promise.resolve(this.templateCache[url]);
        } else {
            req = raw(url, {
                dataType: 'text'
            });
        }

        return req
            .then(function (response) {
                // if its an actual ajax request, just get the response text
                if (response.jqXHR) {
                    response = response.jqXHR.responseText;
                }
                var els = Ember.$(response);

                for (var i = 0; i < els.length; ++i) {
                    if (els[i].nodeName === 'svg') {
                        return els[i];
                    }
                }
            });
    },

    announceIdNotFound: function (id) {
        var msg;

        if (typeof id === 'string') {
            msg = 'icon ' + id + ' not found';
            Ember.Logger.log(msg);
        }

        return Ember.RSVP.Promise.reject(msg || id);
    },

    announceNotFound: function (err) {
        var msg = (typeof err === 'string') ? err : (err.message || err.data || err.statusText);

        return Ember.RSVP.Promise.reject(msg);
    },

    isIcon: function (target) {
        return (typeof target.element !== 'undefined') && (typeof target.config !== 'undefined');
    },

    cacheIcon: function (icon, id) {
        this.iconCache[id] = this.isIcon(icon) ? icon : new Icon(icon, config[id]);
        return this.iconCache[id].clone();
    }

});

export default
IconService;
<commit_msg>preload registered sets<commit_after>import Ember from 'ember';
import {
    raw
    }
    from 'ic-ajax';

var config = {
    defaultViewBoxSize: 24
};

var urlRegex = /[-a-zA-Z0-9@:%_\+.~#?&//=]{2,256}\.[a-z]{2,4}\b(\/[-a-zA-Z0-9@:%_\+.~#?&//=]*)?/i;

function Icon(el, config) {
    if (el.tagName != 'svg') {
        el = Ember.$('<svg xmlns="http://www.w3.org/2000/svg">').append(el)[0];
    }

    el = $(el);

    // inject the namespace if not available...
    if (!el.attr('xmlns')) {
        el.attr('xmlns', "http://www.w3.org/2000/svg");
    }

    this.element = el;
    this.config = config;
    this.prepare();
}

Icon.prototype = {
    clone: cloneSVG,
    prepare: prepareAndStyle
};

/**
 * Clone the Icon DOM element
 */
function cloneSVG() {
    return Ember.$(this.element[0].cloneNode(true));
}

/**
 * Prepare the DOM element that will be cached in the
 * loaded iconCache store.
 */
function prepareAndStyle() {
    var viewBoxSize = this.config ? this.config.viewBoxSize : config.defaultViewBoxSize;
    var svg = this.element;

    svg[0].setAttribute('fit', '');
    svg[0].setAttribute('height', '100%');
    svg[0].setAttribute('width', '100%');
    svg[0].setAttribute('preserveAspectRatio', 'xMidYMid meet');

    svg[0].setAttribute('viewBox', svg[0].getAttribute('viewBox') || ('0 0 ' + viewBoxSize + ' ' + viewBoxSize));

    svg.css({
        'pointer-events': 'none',
        'display': 'block'
    });

    this.element = svg;
}

var IconService = Ember.Service.extend({

    iconCache: {},
    templateCache: {},

    preloadIcons: function () {

        var self = this;

        var svgRegistry = [
            {
                id: 'md-tabs-arrow',
                url: 'md-tabs-arrow.svg',
                svg: '<svg version="1.1" x="0px" y="0px" viewBox="0 0 24 24"><g><polygon points="15.4,7.4 14,6 8,12 14,18 15.4,16.6 10.8,12 "/></g></svg>'

            },
            {
                id: 'md-close',
                url: 'md-close.svg',
                svg: '<svg version="1.1" x="0px" y="0px" viewBox="0 0 24 24"><g><path d="M19 6.41l-1.41-1.41-5.59 5.59-5.59-5.59-1.41 1.41 5.59 5.59-5.59 5.59 1.41 1.41 5.59-5.59 5.59 5.59 1.41-1.41-5.59-5.59z"/></g></svg>'

            },
            {
                id: 'md-cancel',
                url: 'md-cancel.svg',
                svg: '<svg version="1.1" x="0px" y="0px" viewBox="0 0 24 24"><g><path d="M12 2c-5.53 0-10 4.47-10 10s4.47 10 10 10 10-4.47 10-10-4.47-10-10-10zm5 13.59l-1.41 1.41-3.59-3.59-3.59 3.59-1.41-1.41 3.59-3.59-3.59-3.59 1.41-1.41 3.59 3.59 3.59-3.59 1.41 1.41-3.59 3.59 3.59 3.59z"/></g></svg>'

            },
            {
                id: 'md-toggle-arrow',
                url: 'md-toggle-arrow-svg',
                svg: '<svg version="1.1" x="0px" y="0px" viewBox="0 0 48 48"><path d="M24 16l-12 12 2.83 2.83 9.17-9.17 9.17 9.17 2.83-2.83z"/><path d="M0 0h48v48h-48z" fill="none"/></svg>'

            }
        ];

        svgRegistry.forEach(function (asset) {
            //self.icon(asset.id, asset.url);
            self.templateCache[asset.url] = asset.svg;
        });

    },

    init: function () {
        this._super(...arguments);
        this.preloadIcons();
    },

    preloadSets: function(){
        var self = this;

        for(var id in config){
            if(!config[id].svg){
                self.loadFromIconSet(id);
            }
        }
    },

    getIcon: function (id) {
        id = id || '';

        var self = this;

        // if already loaded and cached, use a clone of the cached icon.
        if (this.iconCache[id]) {
            return Ember.RSVP.Promise.resolve(this.iconCache[id].clone());
        }

        if (urlRegex.test(id)) {
            return this.loadByURL(id)
                .then(function (icon) {
                    return self.cacheIcon(icon, id);
                });
        }

        if (id.indexOf(':') == -1) {
            id = '$default:' + id;
        }

        return this.loadByID(id).then(function(icon){
            return self.cacheIcon(icon, id);

        // error -> try load from set
        },function(err){
            return self.loadFromIconSet(id).then(function(icon){
                return self.cacheIcon(icon, id);
            }, function(err){
                //self.announceIdNotFound(id);
            })
        });

    },

    icon: function (id, url, viewBoxSize) {
        if (id.indexOf(':') == -1) {
            id = '$default:' + id;
        }

        config[id] = {
            url: url,
            viewBoxSize: viewBoxSize || config.defaultViewBoxSize
        };
    },

    iconSet: function (id, url, viewBoxSize) {
        config[id] = {
            url: url,
            viewBoxSize: viewBoxSize || config.defaultViewBoxSize
        };
    },

    defaultIconSet: function (url, viewBoxSize) {
        var setName = '$default';

        if (!config[setName]) {
            config[setName] = {
                url: url,
                viewBoxSize: viewBoxSize || config.defaultViewBoxSize
            };
        }
    },

    defaultViewBoxSize: function (viewBoxSize) {
        config.defaultViewBoxSize = viewBoxSize;
    },

    loadByID: function (id) {
        var iconConfig = config[id];

        return !iconConfig ? Ember.RSVP.Promise.reject(id) : this.loadByURL(iconConfig.url).then(function (icon) {
            return new Icon(icon, iconConfig);
        });
    },

    loadFromIconSet: function (id) {

        var self = this;
        var setName = id.substring(0, id.lastIndexOf(':')) || '$default';
        var iconSetConfig = config[setName];

        function extractFromSet(svg) {

            if (!self.templateCache[iconSetConfig.url]) {
                self.templateCache[iconSetConfig.url] = svg;
            }

            var iconName = id.slice(id.lastIndexOf(':') + 1);
            var icon = svg.querySelector('#' + iconName);
            return !icon ? Ember.RSVP.Promise.reject(id) : new Icon(icon, iconSetConfig);
        }

        return !iconSetConfig ? Ember.RSVP.Promise.reject(id) : this.loadByURL(iconSetConfig.url).then(extractFromSet);
    },

    loadByURL: function (url) {

        // first check templateCache

        var req;

        if (this.templateCache[url]) {
            req = Ember.RSVP.Promise.resolve(this.templateCache[url]);
        } else {
            req = raw(url, {
                dataType: 'text'
            });
        }

        return req
            .then(function (response) {
                // if its an actual ajax request, just get the response text
                if (response.jqXHR) {
                    response = response.jqXHR.responseText;
                }
                var els = Ember.$(response);

                for (var i = 0; i < els.length; ++i) {
                    if (els[i].nodeName === 'svg') {
                        return els[i];
                    }
                }
            });
    },

    announceIdNotFound: function (id) {
        var msg;

        if (typeof id === 'string') {
            msg = 'icon ' + id + ' not found';
            Ember.Logger.log(msg);
        }

        return Ember.RSVP.Promise.reject(msg || id);
    },

    announceNotFound: function (err) {
        var msg = (typeof err === 'string') ? err : (err.message || err.data || err.statusText);

        return Ember.RSVP.Promise.reject(msg);
    },

    isIcon: function (target) {
        return (typeof target.element !== 'undefined') && (typeof target.config !== 'undefined');
    },

    cacheIcon: function (icon, id) {
        this.iconCache[id] = this.isIcon(icon) ? icon : new Icon(icon, config[id]);
        return this.iconCache[id].clone();
    }

});

export default
IconService;
<|endoftext|>


<commit_before>module.exports = function(grunt) {
    grunt.util.linefeed = '\n';

    var options = {
        path: {
            tmp: 'tmp',
            src: {
                js: 'js',
                lib: 'lib',
                sass: 'sass',
                node: 'components/node_modules',
                bower: 'components/bower_components'
            },
            dest: {
                js: '../view/js',
                fonts: '../view/fonts',
                img: '../view/img',
                css: '../view/css',
                maps: '../view/maps'
            }
        },

        files: {
            boltJs: [
                '<%= path.src.js %>/console.js',
                '<%= path.src.js %>/fnc-helpers.js',
                '<%= path.src.js %>/activity.js',
                '<%= path.src.js %>/bind-fileupload.js',
                '<%= path.src.js %>/make-uri-slug.js',
                '<%= path.src.js %>/video-embed.js',
                '<%= path.src.js %>/geolocation.js',
                '<%= path.src.js %>/upload-files.js',
                '<%= path.src.js %>/obj-sidebar.js',
                '<%= path.src.js %>/obj-navpopups.js',
                '<%= path.src.js %>/obj-moments.js',
                '<%= path.src.js %>/obj-files.js',
                '<%= path.src.js %>/obj-stack.js',
                '<%= path.src.js %>/obj-folders.js',
                '<%= path.src.js %>/obj-datetime.js',
                '<%= path.src.js %>/obj-validation.js',
                '<%= path.src.js %>/extend.js',
                '<%= path.src.js %>/init.js',
                '<%= path.src.js %>/start.js'
            ]
        },

        banner: {
            boltJs: [
                '/**',
                ' * These are Bolt’s COMPILED JS files!',
                ' * You can edit *.js files in <../lib/bolt/> and then run “grunt updateBolt” to generate this file.',
                ' */'
            ].join('\n'),
            boltCss: [
                '/**',
                ' * These are Bolt’s COMPILED CSS files!',
                ' * Do not edit these files, because all changes will be lost.',
                ' * You can edit *.scss files in <../scss/> and then run “grunt updateBolt” to generate this file.',
                ' */'
            ].join('\n')
        }
    };

    require('load-grunt-config')(grunt, {data: options});

};
<commit_msg>Fix bower/node paths<commit_after>module.exports = function(grunt) {
    grunt.util.linefeed = '\n';

    var options = {
        path: {
            tmp: 'tmp',
            src: {
                js: 'js',
                lib: 'lib',
                sass: 'sass',
                node: 'node_modules',
                bower: 'bower_components'
            },
            dest: {
                js: '../view/js',
                fonts: '../view/fonts',
                img: '../view/img',
                css: '../view/css',
                maps: '../view/maps'
            }
        },

        files: {
            boltJs: [
                '<%= path.src.js %>/console.js',
                '<%= path.src.js %>/fnc-helpers.js',
                '<%= path.src.js %>/activity.js',
                '<%= path.src.js %>/bind-fileupload.js',
                '<%= path.src.js %>/make-uri-slug.js',
                '<%= path.src.js %>/video-embed.js',
                '<%= path.src.js %>/geolocation.js',
                '<%= path.src.js %>/upload-files.js',
                '<%= path.src.js %>/obj-sidebar.js',
                '<%= path.src.js %>/obj-navpopups.js',
                '<%= path.src.js %>/obj-moments.js',
                '<%= path.src.js %>/obj-files.js',
                '<%= path.src.js %>/obj-stack.js',
                '<%= path.src.js %>/obj-folders.js',
                '<%= path.src.js %>/obj-datetime.js',
                '<%= path.src.js %>/obj-validation.js',
                '<%= path.src.js %>/extend.js',
                '<%= path.src.js %>/init.js',
                '<%= path.src.js %>/start.js'
            ]
        },

        banner: {
            boltJs: [
                '/**',
                ' * These are Bolt’s COMPILED JS files!',
                ' * You can edit *.js files in <../lib/bolt/> and then run “grunt updateBolt” to generate this file.',
                ' */'
            ].join('\n'),
            boltCss: [
                '/**',
                ' * These are Bolt’s COMPILED CSS files!',
                ' * Do not edit these files, because all changes will be lost.',
                ' * You can edit *.scss files in <../scss/> and then run “grunt updateBolt” to generate this file.',
                ' */'
            ].join('\n')
        }
    };

    require('load-grunt-config')(grunt, {data: options});

};
<|endoftext|>


<commit_before>// Copyright 2014 Globo.com Player authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

var UIPlugin = require('../../base/ui_plugin')
var Styler = require('../../base/styler')
var JST = require('../../base/jst')
var _ = require("underscore")
var Mediator = require('../../components/mediator')
var Browser = require('../../components/browser')

var objectIE = '<object type="application/x-shockwave-flash" id="<%= cid %>" classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" data-hls=""><param name="movie" value="<%= swfPath %>"> <param name="quality" value="autohigh"> <param name="swliveconnect" value="true"> <param name="allowScriptAccess" value="always"> <param name="bgcolor" value="#001122"> <param name="allowFullScreen" value="false"> <param name="wmode" value="transparent"> <param name="tabindex" value="1"> </object>'

class HLS extends UIPlugin {
  get name() { return 'hls' }
  get tagName() { return 'object' }
  get template() { return JST.hls }
  get attributes() {
    return {
      'data-hls': '',
      'type': 'application/x-shockwave-flash'
    }
  }

  constructor(options) {
    super(options)
    this.src = options.src
    this.swfPath = options.swfPath || "http://cdn.clappr.io/latest/assets/HLSPlayer.swf"
    this.highDefinition = false
    this.autoPlay = options.autoPlay
    this.defaultSettings = {
      left: ["playstop", "volume"],
      default: ['seekbar'],
      right: ["fullscreen", "hd-indicator"],
      seekEnabled: true
    }
    this.settings = _.extend({}, this.defaultSettings)
    this.addListeners()
  }

  addListeners() {
    Mediator.on(this.uniqueId + ':flashready', () => this.bootstrap())
    Mediator.on(this.uniqueId + ':timeupdate', () => this.updateTime())
    Mediator.on(this.uniqueId + ':playbackstate', (state) => this.setPlaybackState(state))
    Mediator.on(this.uniqueId + ':highdefinition', (isHD) => this.updateHighDefinition(isHD))
    Mediator.on(this.uniqueId + ':playbackerror', () => this.flashPlaybackError())
  }

  stopListening() {
    super()
    Mediator.off(this.uniqueId + ':flashready')
    Mediator.off(this.uniqueId + ':timeupdate')
    Mediator.off(this.uniqueId + ':playbackstate')
    Mediator.off(this.uniqueId + ':highdefinition')
    Mediator.off(this.uniqueId + ':playbackerror')
  }

  bootstrap() {
    this.el.width = "100%"
    this.el.height = "100%"
    this.trigger('playback:ready', this.name)
    this.currentState = "IDLE"
    this.el.globoPlayerSetflushLiveURLCache(true)
    this.autoPlay && this.play()
  }

  updateHighDefinition(isHD) {
    this.highDefinition = (isHD === "true");
    this.trigger('playback:highdefinitionupdate')
  }

  updateTime() {
    var duration = this.getDuration()
    var position = this.el.globoGetPosition()
    if (this.playbackType === 'live' && position >= duration) {
      position = duration
    }
    this.trigger('playback:timeupdate', position, duration, this.name)

    var previousDVRStatus = this.dvrEnabled
    this.dvrEnabled = (this.playbackType === 'live' && duration > 240)
    if (this.dvrEnabled !== previousDVRStatus) {
      this.updateSettings()
    } else {
      this.trigger('playback:timeupdate', this.el.globoGetPosition(), duration, this.name)
    }
  }

  play() {
    if(this.currentState === 'PAUSED') {
      this.el.globoPlayerResume()
    } else {
      this.firstPlay()
    }
    this.trigger('playback:play', this.name)
  }

  getPlaybackType() {
    if (this.playbackType)
      return this.playbackType
    return null
  }

  getCurrentBitrate() {
    var currentLevel = this.getLevels()[this.el.globoGetLevel()]
    return currentLevel.bitrate
  }

  getLastProgramDate() {
    var programDate = this.el.globoGetLastProgramDate()
    // normalizing for BRT
    return programDate - 1.08e+7
  }

  isHighDefinitionInUse() {
    return this.highDefinition
  }

  getLevels() {
    if (!this.levels || this.levels.length === 0) {
      this.levels = this.el.globoGetLevels()
    }
    return this.levels
  }

  setPlaybackState(state) {
    var bufferLength = this.el.globoGetbufferLength()
    if (state === "PLAYING_BUFFERING" && bufferLength < 1)  {
      this.trigger('playback:buffering', this.name)
      this.updatePlaybackType()
    } else if (state === "PLAYING") {
      if ((this.currentState === "PLAYING_BUFFERING" || this.currentState === "IDLE") && bufferLength !== this.lastBufferLength) {
        this.trigger('playback:bufferfull', this.name)
        this.updatePlaybackType()
      }
    } else if (state === "IDLE") {
      this.trigger('playback:ended', this.name)
      this.trigger('playback:timeupdate', 0, this.el.globoGetDuration(), this.name)
      this.updatePlaybackType()
    }
    this.lastBufferLength = bufferLength
    this.currentState = state;
  }

  updatePlaybackType() {
    if (!this.playbackType) {
      this.playbackType = this.el.globoGetType()
      if (this.playbackType) {
        this.playbackType = this.playbackType.toLowerCase()
      }
      this.trigger('playback:playbackstate');
    }
  }

  firstPlay() {
    this.el.globoPlayerLoad(this.src)
    this.el.globoPlayerPlay()
  }

  volume(value) {
    this.el.globoPlayerVolume(value)
  }

  pause() {
    this.el.globoPlayerPause()
  }

  stop() {
    this.el.globoPlayerStop()
    this.trigger('playback:timeupdate', 0, this.name)
  }

  isPlaying() {
    if (this.currentState) {
      return !!(this.currentState.match(/playing/i))
    }
    return false
  }

  getDuration() {
    var duration = this.el.globoGetDuration()
    if (this.playbackType === 'live') {
      // estimate 10 seconds of buffer time for live streams for seek positions
      duration = duration - 10
    }
    return duration
  }

  seek(time) {
    var duration = this.getDuration()
    if (time > 0) {
      time = duration * time / 100
    }
    // seek operations to a time within 2 seconds from live stream will position playhead back to live
    if (this.playbackType === 'live' && duration - time < 2) {
      time = -1
    }
    this.el.globoPlayerSeek(time)
  }

  flashPlaybackError() {
    this.trigger('playback:stop')
  }

  timeUpdate(time, duration) {
    this.trigger('playback:timeupdate', time, duration, this.name)
  }

  destroy() {
    this.stopListening()
    this.$el.remove()
  }

  setupFirefox() {
    var $el = this.$('embed')
    $el.attr('data-hls', '')
    this.setElement($el[0])
  }

  setupIE() {
    this.setElement($(_.template(objectIE)({cid: this.cid, swfPath: this.swfPath})))
  }

  updateSettings() {
    this.settings = _.extend({}, this.defaultSettings)
    if (this.playbackType === "vod" || this.dvrEnabled) {
      this.settings.left = ["playpause", "position", "duration"]
      this.settings.default = ["seekbar"]
    } else {
      this.settings.seekEnabled = false
    }
    this.trigger('playback:settingsupdate', this.name)
  }

  render() {
    var style = Styler.getStyleFor(this.name)
    this.$el.html(this.template({cid: this.cid, swfPath: this.swfPath, playbackId: this.uniqueId}))
    this.$el.append(style)
    this.el.id = this.cid
    if(Browser.isFirefox) {
      this.setupFirefox()
    } else if(Browser.isLegacyIE) {
      this.setupIE()
    }
    return this
  }
}

HLS.canPlay = function(resource) {
  return !!resource.match(/^http(.*).m3u8/)
}

module.exports = HLS
<commit_msg>hls: issue event on dvr playback status change (refs #152)<commit_after>// Copyright 2014 Globo.com Player authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

var UIPlugin = require('../../base/ui_plugin')
var Styler = require('../../base/styler')
var JST = require('../../base/jst')
var _ = require("underscore")
var Mediator = require('../../components/mediator')
var Browser = require('../../components/browser')

var objectIE = '<object type="application/x-shockwave-flash" id="<%= cid %>" classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" data-hls=""><param name="movie" value="<%= swfPath %>"> <param name="quality" value="autohigh"> <param name="swliveconnect" value="true"> <param name="allowScriptAccess" value="always"> <param name="bgcolor" value="#001122"> <param name="allowFullScreen" value="false"> <param name="wmode" value="transparent"> <param name="tabindex" value="1"> </object>'

class HLS extends UIPlugin {
  get name() { return 'hls' }
  get tagName() { return 'object' }
  get template() { return JST.hls }
  get attributes() {
    return {
      'data-hls': '',
      'type': 'application/x-shockwave-flash'
    }
  }

  constructor(options) {
    super(options)
    this.src = options.src
    this.swfPath = options.swfPath || "http://cdn.clappr.io/latest/assets/HLSPlayer.swf"
    this.highDefinition = false
    this.autoPlay = options.autoPlay
    this.defaultSettings = {
      left: ["playstop", "volume"],
      default: ['seekbar'],
      right: ["fullscreen", "hd-indicator"],
      seekEnabled: true
    }
    this.settings = _.extend({}, this.defaultSettings)
    this.addListeners()
  }

  addListeners() {
    Mediator.on(this.uniqueId + ':flashready', () => this.bootstrap())
    Mediator.on(this.uniqueId + ':timeupdate', () => this.updateTime())
    Mediator.on(this.uniqueId + ':playbackstate', (state) => this.setPlaybackState(state))
    Mediator.on(this.uniqueId + ':highdefinition', (isHD) => this.updateHighDefinition(isHD))
    Mediator.on(this.uniqueId + ':playbackerror', () => this.flashPlaybackError())
  }

  stopListening() {
    super()
    Mediator.off(this.uniqueId + ':flashready')
    Mediator.off(this.uniqueId + ':timeupdate')
    Mediator.off(this.uniqueId + ':playbackstate')
    Mediator.off(this.uniqueId + ':highdefinition')
    Mediator.off(this.uniqueId + ':playbackerror')
  }

  bootstrap() {
    this.el.width = "100%"
    this.el.height = "100%"
    this.trigger('playback:ready', this.name)
    this.currentState = "IDLE"
    this.el.globoPlayerSetflushLiveURLCache(true)
    this.autoPlay && this.play()
  }

  updateHighDefinition(isHD) {
    this.highDefinition = (isHD === "true");
    this.trigger('playback:highdefinitionupdate')
  }

  updateTime() {
    var duration = this.getDuration()
    var position = this.el.globoGetPosition()
    if (this.playbackType === 'live' && position >= duration) {
      position = duration
    }
    this.trigger('playback:timeupdate', position, duration, this.name)

    var previousDVRStatus = this.dvrEnabled
    this.dvrEnabled = (this.playbackType === 'live' && duration > 240)
    if (this.dvrEnabled !== previousDVRStatus) {
      this.updateSettings()
    }

    var previousDvrInUse = !!this.dvrInUse
    this.dvrInUse = this.dvrEnabled && (duration - position >= 2)
    if (this.dvrInUse !== previousDvrInUse) {
      this.trigger('playback:dvr', this.dvrInUse)
    }
  }

  play() {
    if(this.currentState === 'PAUSED') {
      this.el.globoPlayerResume()
    } else {
      this.firstPlay()
    }
    this.trigger('playback:play', this.name)
  }

  getPlaybackType() {
    if (this.playbackType)
      return this.playbackType
    return null
  }

  getCurrentBitrate() {
    var currentLevel = this.getLevels()[this.el.globoGetLevel()]
    return currentLevel.bitrate
  }

  getLastProgramDate() {
    var programDate = this.el.globoGetLastProgramDate()
    // normalizing for BRT
    return programDate - 1.08e+7
  }

  isHighDefinitionInUse() {
    return this.highDefinition
  }

  getLevels() {
    if (!this.levels || this.levels.length === 0) {
      this.levels = this.el.globoGetLevels()
    }
    return this.levels
  }

  setPlaybackState(state) {
    var bufferLength = this.el.globoGetbufferLength()
    if (state === "PLAYING_BUFFERING" && bufferLength < 1)  {
      this.trigger('playback:buffering', this.name)
      this.updatePlaybackType()
    } else if (state === "PLAYING") {
      if ((this.currentState === "PLAYING_BUFFERING" || this.currentState === "IDLE") && bufferLength !== this.lastBufferLength) {
        this.trigger('playback:bufferfull', this.name)
        this.updatePlaybackType()
      }
    } else if (state === "IDLE") {
      this.trigger('playback:ended', this.name)
      this.trigger('playback:timeupdate', 0, this.el.globoGetDuration(), this.name)
      this.updatePlaybackType()
    }
    this.lastBufferLength = bufferLength
    this.currentState = state;
  }

  updatePlaybackType() {
    if (!this.playbackType) {
      this.playbackType = this.el.globoGetType()
      if (this.playbackType) {
        this.playbackType = this.playbackType.toLowerCase()
      }
      this.trigger('playback:playbackstate');
    }
  }

  firstPlay() {
    this.el.globoPlayerLoad(this.src)
    this.el.globoPlayerPlay()
  }

  volume(value) {
    this.el.globoPlayerVolume(value)
  }

  pause() {
    this.el.globoPlayerPause()
    if (this.playbackType === 'live' && this.dvrEnabled) {
      this.trigger('playback:dvr', true);
    }
  }

  stop() {
    this.el.globoPlayerStop()
    this.trigger('playback:timeupdate', 0, this.name)
  }

  isPlaying() {
    if (this.currentState) {
      return !!(this.currentState.match(/playing/i))
    }
    return false
  }

  getDuration() {
    var duration = this.el.globoGetDuration()
    if (this.playbackType === 'live') {
      // estimate 10 seconds of buffer time for live streams for seek positions
      duration = duration - 10
    }
    return duration
  }

  seek(time) {
    var duration = this.getDuration()
    if (time > 0) {
      time = duration * time / 100
    }
    // seek operations to a time within 2 seconds from live stream will position playhead back to live
    if (this.playbackType === 'live' && duration - time < 2) {
      time = -1
    }
    this.el.globoPlayerSeek(time)
  }

  flashPlaybackError() {
    this.trigger('playback:stop')
  }

  timeUpdate(time, duration) {
    this.trigger('playback:timeupdate', time, duration, this.name)
  }

  destroy() {
    this.stopListening()
    this.$el.remove()
  }

  setupFirefox() {
    var $el = this.$('embed')
    $el.attr('data-hls', '')
    this.setElement($el[0])
  }

  setupIE() {
    this.setElement($(_.template(objectIE)({cid: this.cid, swfPath: this.swfPath})))
  }

  updateSettings() {
    this.settings = _.extend({}, this.defaultSettings)
    if (this.playbackType === "vod" || this.dvrEnabled) {
      this.settings.left = ["playpause", "position", "duration"]
      this.settings.default = ["seekbar"]
    } else {
      this.settings.seekEnabled = false
    }
    this.trigger('playback:settingsupdate', this.name)
  }

  render() {
    var style = Styler.getStyleFor(this.name)
    this.$el.html(this.template({cid: this.cid, swfPath: this.swfPath, playbackId: this.uniqueId}))
    this.$el.append(style)
    this.el.id = this.cid
    if(Browser.isFirefox) {
      this.setupFirefox()
    } else if(Browser.isLegacyIE) {
      this.setupIE()
    }
    return this
  }
}

HLS.canPlay = function(resource) {
  return !!resource.match(/^http(.*).m3u8/)
}

module.exports = HLS
<|endoftext|>