<commit_before>/* This Source Code Form is subject to the terms of the MIT license
 * If a copy of the MIT license was not distributed with this file, you can
 * obtain one at https://raw.github.com/mozilla/butter/master/LICENSE */

define( [ "localized", "util/uri", "json!/api/butterconfig" ],
  function( Localized, URI, config ) {

  var REGEX_MAP = {
        YouTube: /^(?:https?:\/\/www\.|https?:\/\/|www\.|\.|^)youtu/,
        Vimeo: /^https?:\/\/(www\.)?(player\.)?vimeo\.com\/(video\/)?(\d+)/,
        SoundCloud: /^(?:https?:\/\/www\.|https?:\/\/|www\.|\.|^)(w\.)?(soundcloud)/,
        Archive: /^(?:https?:\/\/www\.|https?:\/\/|www\.|\.|^)archive\.org\/(details|download|stream)\/((.*)start(\/|=)[\d\.]+(.*)end(\/|=)[\d\.]+)?/,
        // supports #t=<start>,<duration>
        // where start or duration can be: X, X.X or XX:XX
        "null": /^\s*#t=(?:\d*(?:(?:\.|\:)?\d+)?),?(\d+(?:(?:\.|\:)\d+)?)\s*$/,
        Flickr: /^https?:\/\/(www\.)flickr.com/
      },
      YOUTUBE_EMBED_DISABLED = Localized.get ( "Embedding of this YouTube video is disabled" ),
      YOUTUBE_EMBED_UNPLAYABLE = Localized.get( "This YouTube video is unplayable" ),
      YOUTUBE_EMBED_PRIVATE = Localized.get( "Private Video" ),
      ARCHIVE_EMBED_DISABLED = Localized.get( "Embedding of this Archive item is not available yet" ),
      EMBED_UNPLAYABLE = Localized.get( "This media source is unplayable" ),
      SOUNDCLOUD_EMBED_DISABLED = Localized.get( "Embedding of this SoundCloud audio source is disabled" );

  var nodeHubbleEndpoint = config.node_hubble_endpoint;

  function jwPlayerFallback( options, successCallback, errorCallback ) {
    // We hit an error trying to load HTML5, try the jwplayer instead
    var media,
        div = document.createElement( "div" ),
        container = document.createElement( "div" );

    div.style.height = "400px";
    div.style.width = "400px";
    div.style.left = "-400px";
    div.style.position = "absolute";
    container.style.height = "100%";
    container.style.width = "100%";

    document.body.appendChild( div );
    div.appendChild( container );

    function errorEvent() {
      media.removeEventListener( "loadedmetadata", readyEvent, false );
      media.removeEventListener( "error", errorEvent, false );
      errorCallback( EMBED_UNPLAYABLE );
      document.body.removeChild( div );
    }

    function readyEvent() {
      media.removeEventListener( "loadedmetadata", readyEvent, false );
      media.removeEventListener( "error", errorEvent, false );
      document.body.removeChild( div );
      successCallback({
        source: options.source,
        title: options.title || options.source,
        type: options.type,
        thumbnail: options.thumbnail || "",
        linkback: options.linkback,
        duration: media.duration
      });
    }
    container.id = Popcorn.guid( "popcorn-jwplayer-" );
    media = Popcorn.HTMLJWPlayerVideoElement( container );
    media.addEventListener( "error", errorEvent, false );
    media.addEventListener( "loadedmetadata", readyEvent, false );
    media.src = options.source;
  }

  return {
    checkUrl: function( url ) {
      for ( var type in REGEX_MAP ) {
        if ( REGEX_MAP.hasOwnProperty( type ) ) {
          if ( REGEX_MAP[ type ].test( url ) ) {
            return type;
          }
        }
      }
      return "HTML5";
    },
    getMetaData: function( baseUrl, successCallback, errorCallback ) {
      // Ensure that we don't wind up having strings decoded twice.
      baseUrl = decodeURI( baseUrl );

      var id,
          userId,
          parsedUri,
          splitUriDirectory,
          xhrURL,
          type = this.checkUrl( baseUrl ),
          videoElem;

      successCallback = successCallback || function(){};
      errorCallback = errorCallback || function(){};

      if ( type === "YouTube" ) {
        parsedUri = URI.parse( baseUrl );
        // youtube id can either be a query under v, example:
        // http://www.youtube.com/watch?v=p_7Qi3mprKQ
        // Or at the end of the url like this:
        // http://youtu.be/p_7Qi3mprKQ
        // and:
        // http://www.youtube.com/embed/p_7Qi3mprKQ
        id = parsedUri.queryKey.v || parsedUri.directory.replace( /\/(embed\/)?/, "" );
        if ( !id ) {
          return;
        }

        xhrURL = "https://gdata.youtube.com/feeds/api/videos/" + id + "?v=2&alt=jsonc&callback=?";
        Popcorn.getJSONP( xhrURL, function( resp ) {
          var respData = resp.data,
              from = parsedUri.queryKey.t,
              popcorn,
              div = document.createElement( "div" ),
              source;

          div.style.height = "400px";
          div.style.width = "400px";
          div.style.left = "-400px";
          div.style.position = "absolute";

          document.body.appendChild( div );

          if ( resp.error ) {
            if ( resp.error.code === 403 ){
              return errorCallback( YOUTUBE_EMBED_PRIVATE );
            }
            errorCallback( YOUTUBE_EMBED_UNPLAYABLE );
          }

          if ( !respData ) {
            return;
          }

          if ( respData.accessControl.embed === "denied" ) {
            errorCallback( YOUTUBE_EMBED_DISABLED );
            return;
          }

          function errorEvent() {
            popcorn.off( "loadedmetadata", readyEvent );
            popcorn.off( "error", errorEvent );
            errorCallback( YOUTUBE_EMBED_UNPLAYABLE );
            popcorn.destroy();
          }

          function readyEvent() {
            popcorn.off( "loadedmetadata", readyEvent );
            popcorn.off( "error", errorEvent );
            document.body.removeChild( div );
            popcorn.destroy();

            successCallback({
              source: source,
              title: respData.title,
              type: type,
              thumbnail: respData.thumbnail.hqDefault,
              author: respData.uploader,
              duration: popcorn.duration(),
              from: from
            });
          }

          if ( from ) {
            from = from.replace( /(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s)?/, function( all, hours, minutes, seconds ) {
              // Make sure we have real zeros
              hours = hours | 0; // bit-wise OR
              minutes = minutes | 0; // bit-wise OR
              seconds = seconds | 0; // bit-wise OR
              return ( +seconds + ( ( ( hours * 60 ) + minutes ) * 60 ) );
            });
          }

          source = "http://www.youtube.com/watch?v=" + id;
          popcorn = Popcorn.smart( div, source );
          popcorn.on( "error", errorEvent );
          if ( popcorn.media.readyState >= 1 ) {
            readyEvent();
          } else {
            popcorn.on( "loadedmetadata", readyEvent );
          }
        });
      } else if ( type === "SoundCloud" ) {
        parsedUri = URI.parse( baseUrl );

        if ( parsedUri.host === "soundcloud.com" ) {
          splitUriDirectory = parsedUri.directory.split( "/" );
          id = splitUriDirectory[ splitUriDirectory.length - 1 ];
          userId = splitUriDirectory[ splitUriDirectory.length - 2 ];

          xhrURL = "https://api.soundcloud.com/tracks/" + id + ".json?callback=?&client_id=PRaNFlda6Bhf5utPjUsptg&user_id=" + userId;
        // If an embed iframe source is used, which looks like this:
        // https://w.soundcloud.com/player/?url=https://api.soundcloud.com/tracks/11921587
        } else if ( parsedUri.host === "w.soundcloud.com" ) {
          id = parsedUri.queryKey.url.split( "api.soundcloud.com/tracks/" )[ 1 ];
          xhrURL = "https://api.soundcloud.com/tracks/" + id + ".json?callback=?&client_id=PRaNFlda6Bhf5utPjUsptg";
        }
        Popcorn.getJSONP( xhrURL, function( respData ) {
          if ( !respData ) {
            return;
          }

          if ( respData.sharing === "private" || respData.embeddable_by === "none" ) {
            errorCallback( SOUNDCLOUD_EMBED_DISABLED );
            return;
          }
          successCallback({
            source: respData.permalink_url || baseUrl,
            type: type,
            thumbnail: respData.artwork_url || "../../resources/icons/soundcloud-small.png",
            duration: respData.duration / 1000,
            title: respData.title,
            hidden: true
          });
        });
      } else if ( type === "Vimeo" ) {
        parsedUri = URI.parse( baseUrl );
        splitUriDirectory = parsedUri.directory.split( "/" );
        id = splitUriDirectory[ splitUriDirectory.length - 1 ];
        xhrURL = "https://vimeo.com/api/v2/video/" + id + ".json?callback=?";
        Popcorn.getJSONP( xhrURL, function( respData ) {
          var source = "http://vimeo.com/" + id;
          respData = respData && respData[ 0 ];
          if ( !respData ) {
            return;
          }
          successCallback({
            source: source,
            type: type,
            thumbnail: respData.thumbnail_small,
            duration: respData.duration,
            title: respData.title
          });
        });
      } else if ( type === "Archive" ) {
        // We don't accept direct MP4/OGV links to videos since Archive.org doesn't want to directly
        // expose the main video's. Until noted, keep it this way and don't change this.
        // Basically always check that any valid types are not included so we can continue to
        // prevent direct video links being pasted in and attributed as HTML5 video.
        if ( baseUrl.indexOf( "details" ) === -1 && baseUrl.indexOf( "stream" ) === -1 ) {
          return errorCallback( ARCHIVE_EMBED_DISABLED );
        }

        xhrURL = "https://archive.org/services/maker.php?callback=caller&url=" + encodeURIComponent( baseUrl );

        Popcorn.getJSONP( xhrURL, function( respData ) {

          if ( !respData || respData.error || !respData.title || !respData.duration ) {
            return errorCallback( ARCHIVE_EMBED_DISABLED );
          }

          videoElem = document.createElement( "video" );
          videoElem.addEventListener( "error", function() {
            var options = {
              source: respData.media,
              type: type,
              title: respData.title,
              thumbnail: respData.thumb,
              linkback: respData.linkback
            };
            jwPlayerFallback( options, successCallback, errorCallback );
          }, false );
          videoElem.addEventListener( "loadedmetadata", function() {
            successCallback({
              source: respData.media,
              type: type,
              title: respData.title,
              thumbnail: respData.thumb,
              linkback: respData.linkback,
              duration: videoElem.duration
            });
          }, false );
          videoElem.src = URI.makeUnique( respData.media ).toString();
        });
      } else if ( type === "null" ) {
        successCallback({
          source: baseUrl,
          type: type,
          title: baseUrl,
          duration: +REGEX_MAP[ "null" ].exec( baseUrl )[ 1 ]
        });
      } else {
        var title = baseUrl.substring( baseUrl.lastIndexOf( "/" ) + 1 ),
            mediaElem,
            errorOptions,
            successOptions;

        baseUrl = encodeURI( baseUrl );

        errorOptions = {
          source: baseUrl,
          type: type,
          title: title
        };

        successOptions = {
          source: baseUrl,
          type: type,
          title: title,
          thumbnail: URI.makeUnique( baseUrl ).toString()
        };

        Popcorn.getJSONP( nodeHubbleEndpoint + "mime/" + baseUrl, function( resp ) {
          var contentType = resp.contentType;

          successOptions.contentType = errorOptions.contentType = contentType;

          if ( contentType.indexOf( "video" ) === 0 ) {
            mediaElem = document.createElement( "video" );
          } else if ( contentType.indexOf( "audio" ) === 0 || contentType.indexOf( "application/ogg" ) === 0 ) {
            mediaElem = document.createElement( "audio" );
            successOptions.hidden = errorOptions.hidden = true;
          } else if ( contentType.indexOf( "image" ) === 0 ) {
            successCallback({
              source: baseUrl,
              type: "image",
              thumbnail: baseUrl,
              title: baseUrl,
              contentType: contentType,
              duration: 5
            });
            return;
          }

          if ( mediaElem ) {
            mediaElem.addEventListener( "loadedmetadata", function() {
              successOptions.duration = mediaElem.duration;
              successCallback( successOptions );
            }, false );
            mediaElem.addEventListener( "error", function() {
              jwPlayerFallback( errorOptions, successCallback, errorCallback );
            }, false );
            mediaElem.src = URI.makeUnique( baseUrl ).toString();
          } else {
            errorCallback( EMBED_UNPLAYABLE );
          }
        });
      }
    }
  };
});
<commit_msg>Fix Bug 962650 - Fix issue where SoundCloud clips had incorrect data due to multiple clips with same title<commit_after>/* This Source Code Form is subject to the terms of the MIT license
 * If a copy of the MIT license was not distributed with this file, you can
 * obtain one at https://raw.github.com/mozilla/butter/master/LICENSE */

define( [ "localized", "util/uri", "json!/api/butterconfig" ],
  function( Localized, URI, config ) {

  var REGEX_MAP = {
        YouTube: /^(?:https?:\/\/www\.|https?:\/\/|www\.|\.|^)youtu/,
        Vimeo: /^https?:\/\/(www\.)?(player\.)?vimeo\.com\/(video\/)?(\d+)/,
        SoundCloud: /^(?:https?:\/\/www\.|https?:\/\/|www\.|\.|^)(w\.)?(soundcloud)/,
        Archive: /^(?:https?:\/\/www\.|https?:\/\/|www\.|\.|^)archive\.org\/(details|download|stream)\/((.*)start(\/|=)[\d\.]+(.*)end(\/|=)[\d\.]+)?/,
        // supports #t=<start>,<duration>
        // where start or duration can be: X, X.X or XX:XX
        "null": /^\s*#t=(?:\d*(?:(?:\.|\:)?\d+)?),?(\d+(?:(?:\.|\:)\d+)?)\s*$/,
        Flickr: /^https?:\/\/(www\.)flickr.com/
      },
      YOUTUBE_EMBED_DISABLED = Localized.get ( "Embedding of this YouTube video is disabled" ),
      YOUTUBE_EMBED_UNPLAYABLE = Localized.get( "This YouTube video is unplayable" ),
      YOUTUBE_EMBED_PRIVATE = Localized.get( "Private Video" ),
      ARCHIVE_EMBED_DISABLED = Localized.get( "Embedding of this Archive item is not available yet" ),
      EMBED_UNPLAYABLE = Localized.get( "This media source is unplayable" ),
      SOUNDCLOUD_EMBED_DISABLED = Localized.get( "Embedding of this SoundCloud audio source is disabled" );

  var nodeHubbleEndpoint = config.node_hubble_endpoint;

  function jwPlayerFallback( options, successCallback, errorCallback ) {
    // We hit an error trying to load HTML5, try the jwplayer instead
    var media,
        div = document.createElement( "div" ),
        container = document.createElement( "div" );

    div.style.height = "400px";
    div.style.width = "400px";
    div.style.left = "-400px";
    div.style.position = "absolute";
    container.style.height = "100%";
    container.style.width = "100%";

    document.body.appendChild( div );
    div.appendChild( container );

    function errorEvent() {
      media.removeEventListener( "loadedmetadata", readyEvent, false );
      media.removeEventListener( "error", errorEvent, false );
      errorCallback( EMBED_UNPLAYABLE );
      document.body.removeChild( div );
    }

    function readyEvent() {
      media.removeEventListener( "loadedmetadata", readyEvent, false );
      media.removeEventListener( "error", errorEvent, false );
      document.body.removeChild( div );
      successCallback({
        source: options.source,
        title: options.title || options.source,
        type: options.type,
        thumbnail: options.thumbnail || "",
        linkback: options.linkback,
        duration: media.duration
      });
    }
    container.id = Popcorn.guid( "popcorn-jwplayer-" );
    media = Popcorn.HTMLJWPlayerVideoElement( container );
    media.addEventListener( "error", errorEvent, false );
    media.addEventListener( "loadedmetadata", readyEvent, false );
    media.src = options.source;
  }

  return {
    checkUrl: function( url ) {
      for ( var type in REGEX_MAP ) {
        if ( REGEX_MAP.hasOwnProperty( type ) ) {
          if ( REGEX_MAP[ type ].test( url ) ) {
            return type;
          }
        }
      }
      return "HTML5";
    },
    getMetaData: function( baseUrl, successCallback, errorCallback ) {
      // Ensure that we don't wind up having strings decoded twice.
      baseUrl = decodeURI( baseUrl );

      var id,
          userId,
          parsedUri,
          splitUriDirectory,
          xhrURL,
          type = this.checkUrl( baseUrl ),
          videoElem;

      successCallback = successCallback || function(){};
      errorCallback = errorCallback || function(){};

      if ( type === "YouTube" ) {
        parsedUri = URI.parse( baseUrl );
        // youtube id can either be a query under v, example:
        // http://www.youtube.com/watch?v=p_7Qi3mprKQ
        // Or at the end of the url like this:
        // http://youtu.be/p_7Qi3mprKQ
        // and:
        // http://www.youtube.com/embed/p_7Qi3mprKQ
        id = parsedUri.queryKey.v || parsedUri.directory.replace( /\/(embed\/)?/, "" );
        if ( !id ) {
          return;
        }

        xhrURL = "https://gdata.youtube.com/feeds/api/videos/" + id + "?v=2&alt=jsonc&callback=?";
        Popcorn.getJSONP( xhrURL, function( resp ) {
          var respData = resp.data,
              from = parsedUri.queryKey.t,
              popcorn,
              div = document.createElement( "div" ),
              source;

          div.style.height = "400px";
          div.style.width = "400px";
          div.style.left = "-400px";
          div.style.position = "absolute";

          document.body.appendChild( div );

          if ( resp.error ) {
            if ( resp.error.code === 403 ){
              return errorCallback( YOUTUBE_EMBED_PRIVATE );
            }
            errorCallback( YOUTUBE_EMBED_UNPLAYABLE );
          }

          if ( !respData ) {
            return;
          }

          if ( respData.accessControl.embed === "denied" ) {
            errorCallback( YOUTUBE_EMBED_DISABLED );
            return;
          }

          function errorEvent() {
            popcorn.off( "loadedmetadata", readyEvent );
            popcorn.off( "error", errorEvent );
            errorCallback( YOUTUBE_EMBED_UNPLAYABLE );
            popcorn.destroy();
          }

          function readyEvent() {
            popcorn.off( "loadedmetadata", readyEvent );
            popcorn.off( "error", errorEvent );
            document.body.removeChild( div );
            popcorn.destroy();

            successCallback({
              source: source,
              title: respData.title,
              type: type,
              thumbnail: respData.thumbnail.hqDefault,
              author: respData.uploader,
              duration: popcorn.duration(),
              from: from
            });
          }

          if ( from ) {
            from = from.replace( /(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s)?/, function( all, hours, minutes, seconds ) {
              // Make sure we have real zeros
              hours = hours | 0; // bit-wise OR
              minutes = minutes | 0; // bit-wise OR
              seconds = seconds | 0; // bit-wise OR
              return ( +seconds + ( ( ( hours * 60 ) + minutes ) * 60 ) );
            });
          }

          source = "http://www.youtube.com/watch?v=" + id;
          popcorn = Popcorn.smart( div, source );
          popcorn.on( "error", errorEvent );
          if ( popcorn.media.readyState >= 1 ) {
            readyEvent();
          } else {
            popcorn.on( "loadedmetadata", readyEvent );
          }
        });
      } else if ( type === "SoundCloud" ) {
        parsedUri = URI.parse( baseUrl );

        if ( parsedUri.host === "soundcloud.com" ) {
          splitUriDirectory = parsedUri.directory.split( "/" );
          id = splitUriDirectory[ splitUriDirectory.length - 1 ];
          userId = splitUriDirectory[ splitUriDirectory.length - 2 ];

          xhrURL = "https://api.soundcloud.com/tracks.json?callback=?&client_id=PRaNFlda6Bhf5utPjUsptg&title=" + id + "&user_id=" + userId;
        // If an embed iframe source is used, which looks like this:
        // https://w.soundcloud.com/player/?url=https://api.soundcloud.com/tracks/11921587
        } else if ( parsedUri.host === "w.soundcloud.com" ) {
          id = parsedUri.queryKey.url.split( "api.soundcloud.com/tracks/" )[ 1 ];
          xhrURL = "https://api.soundcloud.com/tracks/" + id + ".json?callback=?&client_id=PRaNFlda6Bhf5utPjUsptg";
        }
        Popcorn.getJSONP( xhrURL, function( respData ) {
          if ( !respData || !respData[ 0 ] ) {
            return;
          }

          respData = respData[ 0 ];

          if ( respData.sharing === "private" || respData.embeddable_by === "none" ) {
            errorCallback( SOUNDCLOUD_EMBED_DISABLED );
            return;
          }
          successCallback({
            source: respData.permalink_url || baseUrl,
            type: type,
            thumbnail: respData.artwork_url || "../../resources/icons/soundcloud-small.png",
            duration: respData.duration / 1000,
            title: respData.title,
            hidden: true
          });
        });
      } else if ( type === "Vimeo" ) {
        parsedUri = URI.parse( baseUrl );
        splitUriDirectory = parsedUri.directory.split( "/" );
        id = splitUriDirectory[ splitUriDirectory.length - 1 ];
        xhrURL = "https://vimeo.com/api/v2/video/" + id + ".json?callback=?";
        Popcorn.getJSONP( xhrURL, function( respData ) {
          var source = "http://vimeo.com/" + id;
          respData = respData && respData[ 0 ];
          if ( !respData ) {
            return;
          }
          successCallback({
            source: source,
            type: type,
            thumbnail: respData.thumbnail_small,
            duration: respData.duration,
            title: respData.title
          });
        });
      } else if ( type === "Archive" ) {
        // We don't accept direct MP4/OGV links to videos since Archive.org doesn't want to directly
        // expose the main video's. Until noted, keep it this way and don't change this.
        // Basically always check that any valid types are not included so we can continue to
        // prevent direct video links being pasted in and attributed as HTML5 video.
        if ( baseUrl.indexOf( "details" ) === -1 && baseUrl.indexOf( "stream" ) === -1 ) {
          return errorCallback( ARCHIVE_EMBED_DISABLED );
        }

        xhrURL = "https://archive.org/services/maker.php?callback=caller&url=" + encodeURIComponent( baseUrl );

        Popcorn.getJSONP( xhrURL, function( respData ) {

          if ( !respData || respData.error || !respData.title || !respData.duration ) {
            return errorCallback( ARCHIVE_EMBED_DISABLED );
          }

          videoElem = document.createElement( "video" );
          videoElem.addEventListener( "error", function() {
            var options = {
              source: respData.media,
              type: type,
              title: respData.title,
              thumbnail: respData.thumb,
              linkback: respData.linkback
            };
            jwPlayerFallback( options, successCallback, errorCallback );
          }, false );
          videoElem.addEventListener( "loadedmetadata", function() {
            successCallback({
              source: respData.media,
              type: type,
              title: respData.title,
              thumbnail: respData.thumb,
              linkback: respData.linkback,
              duration: videoElem.duration
            });
          }, false );
          videoElem.src = URI.makeUnique( respData.media ).toString();
        });
      } else if ( type === "null" ) {
        successCallback({
          source: baseUrl,
          type: type,
          title: baseUrl,
          duration: +REGEX_MAP[ "null" ].exec( baseUrl )[ 1 ]
        });
      } else {
        var title = baseUrl.substring( baseUrl.lastIndexOf( "/" ) + 1 ),
            mediaElem,
            errorOptions,
            successOptions;

        baseUrl = encodeURI( baseUrl );

        errorOptions = {
          source: baseUrl,
          type: type,
          title: title
        };

        successOptions = {
          source: baseUrl,
          type: type,
          title: title,
          thumbnail: URI.makeUnique( baseUrl ).toString()
        };

        Popcorn.getJSONP( nodeHubbleEndpoint + "mime/" + baseUrl, function( resp ) {
          var contentType = resp.contentType;

          successOptions.contentType = errorOptions.contentType = contentType;

          if ( contentType.indexOf( "video" ) === 0 ) {
            mediaElem = document.createElement( "video" );
          } else if ( contentType.indexOf( "audio" ) === 0 || contentType.indexOf( "application/ogg" ) === 0 ) {
            mediaElem = document.createElement( "audio" );
            successOptions.hidden = errorOptions.hidden = true;
          } else if ( contentType.indexOf( "image" ) === 0 ) {
            successCallback({
              source: baseUrl,
              type: "image",
              thumbnail: baseUrl,
              title: baseUrl,
              contentType: contentType,
              duration: 5
            });
            return;
          }

          if ( mediaElem ) {
            mediaElem.addEventListener( "loadedmetadata", function() {
              successOptions.duration = mediaElem.duration;
              successCallback( successOptions );
            }, false );
            mediaElem.addEventListener( "error", function() {
              jwPlayerFallback( errorOptions, successCallback, errorCallback );
            }, false );
            mediaElem.src = URI.makeUnique( baseUrl ).toString();
          } else {
            errorCallback( EMBED_UNPLAYABLE );
          }
        });
      }
    }
  };
});
<|endoftext|>


<commit_before>angular.module('os.biospecimen.participant')
  .factory('ImportUtil', function($translate, $injector) {
    var pluginTypes = {};

    function addPluginTypes(importTypes, group, entityType) {
      var types = pluginTypes[entityType];
      if (!types || types.length == 0) {
        return;
      }

      angular.forEach(types,
        function(type) {
          type.group = group;
          importTypes.push(type);
        }
      );

      return importTypes;
    }

    function addForms(importTypes, group, entityType, forms) {
      angular.forEach(forms, function(form) {
        if (form.sysForm) {
          return;
        }

        importTypes.push({
          group: group,
          type: 'extensions',
          title: form.caption,
          params: {
            entityType: entityType,
            formName: form.name
          },
          showUpsert: true
        });
      });

      return importTypes;
    }

    function getParticipantTypes(entityForms, cp, addConsent) {
      var group = $translate.instant('participant.title');

      var importTypes = [];
      if (cp.id == -1) {
        importTypes = [
          {
            group: group, type: 'cprMultiple', title: 'participant.registrations',
            showImportType: true, importType: 'CREATE', showUpsert: true
          }
        ]
      } else {
        importTypes = [
          {
            group: group, type: 'cpr', title: 'participant.list', showUpsert: true
          }
        ]
      }

      if (addConsent) {
        importTypes = importTypes.concat(getConsentTypes(cp, true));
      }

      addPluginTypes(importTypes, group, 'Participant');
      addForms(importTypes, group, 'CommonParticipant', entityForms['CommonParticipant']);
      return addForms(importTypes, group, 'Participant', entityForms['Participant']);
    }

    function getConsentTypes(cp, participantLevel) {
      var group = $translate.instant(participantLevel ? 'participant.title' : 'visits.title');
      if ($injector.has('ecDocument')) {
        if (cp.id != -1) {
          if (cp.visitLevelConsents && participantLevel) {
            return [];
          } else if (!cp.visitLevelConsents && !participantLevel) {
            return [];
          }
        }

        return [{
          group: group, type: 'econsentsDocumentResponse', title: 'participant.consents',
          showImportType: false, importType: 'UPDATE'
        }];
      } else if (participantLevel) {
        return [{
          group: group, type: 'consent', title: 'participant.consents',
          showImportType: false, csvType: 'MULTIPLE_ROWS_PER_OBJ', importType: 'UPDATE'
        }];
      }
    }

    function getVisitTypes(entityForms, cp, addConsent) {
      var group = $translate.instant('visits.title');
      var importTypes = [{ group: group, type: 'visit', title: 'visits.list', showUpsert: true }];

      if (addConsent) {
        importTypes = importTypes.concat(getConsentTypes(cp, false));
      }

      addPluginTypes(importTypes, group, 'SpecimenCollectionGroup');
      return addForms(importTypes, group, 'SpecimenCollectionGroup', entityForms['SpecimenCollectionGroup']);
    }

    function getSpecimenTypes(cp, allowedEntityTypes, entityForms) {
      var group = $translate.instant('specimens.title');

      var importTypes = [];

      importTypes.push({ group: group, type: 'specimen', title: 'specimens.list', showUpsert: true });

      if (allowedEntityTypes.indexOf('DerivativeAndAliquots') != -1) {
        importTypes.push({
          group: group, type: 'specimenAliquot', title: 'specimens.spmn_aliquots',
          showImportType: false, importType    : 'CREATE'
        });
        importTypes.push({
          group: group, type: 'specimenDerivative', title: 'specimens.spmn_derivatives',
          showImportType: false, importType    : 'CREATE'
        });
      }

      if (!cp.specimenCentric) {
        importTypes.push({
          group: group, type: 'masterSpecimen', title: 'participant.master_specimens',
          showImportType: false, importType    : 'CREATE', showUpsert: true
        });
      }

      if (cp.id == -1) {
        importTypes.push({
          group: group, type: 'containerSpecimen', title: 'specimens.container_specimens',
          showImportType: false, importType: 'UPDATE'
        });
      }

      importTypes.push({
        group: group, type: 'specimenDisposal', title: 'participant.specimen_disposal',
        showImportType: false, importType    : 'UPDATE'
      });

      addPluginTypes(importTypes, group, 'Specimen');
      addForms(importTypes, group, 'Specimen', entityForms['Specimen']);
      addForms(importTypes, group, 'SpecimenEvent', entityForms['SpecimenEvent']);
      return addEventTypes(cp, importTypes, group);
    }

    function addEventTypes(cp, importTypes, group) {
      if (cp.id != -1) {
        return importTypes;
      }

      var events = [
        'containerTransferEvent', 'specimenDisposalEvent',
        'specimenReservedEvent', 'specimenReservationCancelEvent',
        'specimenReturnEvent', 'specimenTransferEvent'
      ];

      angular.forEach(events,
        function(event) {
          importTypes.push({
            group: group,
            type: event,
            title: 'bulk_imports.object_types.' + event,
            showImportType: false,
            importType: 'UPDATE',
            params: {}
          });
        }
      );

      return importTypes;
    }

    function getImportDetail(cp, allowedEntityTypes, forms) {
      var breadcrumbs, onSuccess;
      if (cp.id == -1) {
        breadcrumbs = [{state: 'cp-list', title: "cp.list"}];
        onSuccess = {state: 'import-multi-cp-jobs'};
      } else {
        breadcrumbs = [{state: 'cp-list-view', title: cp.shortTitle, params: '{cpId:' + cp.id + '}'}];
        onSuccess = {state: 'import-cp-jobs', params: {cpId: cp.id}};
      }

      var entityForms = {};
      angular.forEach(forms, function(form) {
        if (!entityForms[form.entityType]) {
          entityForms[form.entityType] = [];
        }

        entityForms[form.entityType].push(form);
      });

      var importTypes = [];
      if (!cp.specimenCentric && allowedEntityTypes.indexOf('Participant') >= 0) {
        importTypes = importTypes.concat(getParticipantTypes(entityForms, cp, allowedEntityTypes.indexOf('Consent') >= 0));
      } else if (!cp.specimenCentric && allowedEntityTypes.indexOf('Consent') >= 0) {
        importTypes = importTypes.concat(getConsentTypes(cp, true));
      }

      if (!cp.specimenCentric && allowedEntityTypes.indexOf('SpecimenCollectionGroup') >= 0) {
        importTypes = importTypes.concat(getVisitTypes(entityForms, cp, allowedEntityTypes.indexOf('Consent') >= 0));
      }

      if (allowedEntityTypes.indexOf('Specimen') >= 0) {
        importTypes = importTypes.concat(getSpecimenTypes(cp, allowedEntityTypes, entityForms));
      }

      angular.forEach(importTypes,
        function(importType) {
          if (!importType.params) {
            importType.params = {};
          }

          importType.params.cpId = cp.id;
        }
      );

      return {
        breadcrumbs: breadcrumbs,
        title: 'Bulk Import Records',
        objectType: undefined,
        onSuccess: onSuccess,
        types: importTypes,
        objectParams: {
          cpId: cp.id
        }
      };
    }

    return {
      getImportDetail: function(cp, allowedEntityTypes, forms) {
        return $translate('common.none').then(
          function() {
            return getImportDetail(cp, allowedEntityTypes, forms);
          }
        );
      },

      registerTypes: function(entityType, types) {
        if (!types) {
          delete pluginTypes[entityType];
        } else {
          pluginTypes[entityType] = types;
        }
      },

      getPluginTypes: function() {
        var result = [];
        angular.forEach(pluginTypes,
          function(types, key) {
            angular.forEach(types,
              function(type) {
                result.push(type.type);
              }
            );
          }
        );

        return result;
      }
    }
  });
<commit_msg>Fixed the CP based import menu. The import page was not displayed when econsents plugin was not installed.<commit_after>angular.module('os.biospecimen.participant')
  .factory('ImportUtil', function($translate, $injector) {
    var pluginTypes = {};

    function addPluginTypes(importTypes, group, entityType) {
      var types = pluginTypes[entityType];
      if (!types || types.length == 0) {
        return;
      }

      angular.forEach(types,
        function(type) {
          type.group = group;
          importTypes.push(type);
        }
      );

      return importTypes;
    }

    function addForms(importTypes, group, entityType, forms) {
      angular.forEach(forms, function(form) {
        if (form.sysForm) {
          return;
        }

        importTypes.push({
          group: group,
          type: 'extensions',
          title: form.caption,
          params: {
            entityType: entityType,
            formName: form.name
          },
          showUpsert: true
        });
      });

      return importTypes;
    }

    function getParticipantTypes(entityForms, cp, addConsent) {
      var group = $translate.instant('participant.title');

      var importTypes = [];
      if (cp.id == -1) {
        importTypes = [
          {
            group: group, type: 'cprMultiple', title: 'participant.registrations',
            showImportType: true, importType: 'CREATE', showUpsert: true
          }
        ]
      } else {
        importTypes = [
          {
            group: group, type: 'cpr', title: 'participant.list', showUpsert: true
          }
        ]
      }

      if (addConsent) {
        importTypes = importTypes.concat(getConsentTypes(cp, true));
      }

      addPluginTypes(importTypes, group, 'Participant');
      addForms(importTypes, group, 'CommonParticipant', entityForms['CommonParticipant']);
      return addForms(importTypes, group, 'Participant', entityForms['Participant']);
    }

    function getConsentTypes(cp, participantLevel) {
      var group = $translate.instant(participantLevel ? 'participant.title' : 'visits.title');
      if ($injector.has('ecDocument')) {
        if (cp.id != -1) {
          if (cp.visitLevelConsents && participantLevel) {
            return [];
          } else if (!cp.visitLevelConsents && !participantLevel) {
            return [];
          }
        }

        return [{
          group: group, type: 'econsentsDocumentResponse', title: 'participant.consents',
          showImportType: false, importType: 'UPDATE'
        }];
      } else if (participantLevel) {
        return [{
          group: group, type: 'consent', title: 'participant.consents',
          showImportType: false, csvType: 'MULTIPLE_ROWS_PER_OBJ', importType: 'UPDATE'
        }];
      } else {
        return [];
      }
    }

    function getVisitTypes(entityForms, cp, addConsent) {
      var group = $translate.instant('visits.title');
      var importTypes = [{ group: group, type: 'visit', title: 'visits.list', showUpsert: true }];

      if (addConsent) {
        importTypes = importTypes.concat(getConsentTypes(cp, false));
      }

      addPluginTypes(importTypes, group, 'SpecimenCollectionGroup');
      return addForms(importTypes, group, 'SpecimenCollectionGroup', entityForms['SpecimenCollectionGroup']);
    }

    function getSpecimenTypes(cp, allowedEntityTypes, entityForms) {
      var group = $translate.instant('specimens.title');

      var importTypes = [];

      importTypes.push({ group: group, type: 'specimen', title: 'specimens.list', showUpsert: true });

      if (allowedEntityTypes.indexOf('DerivativeAndAliquots') != -1) {
        importTypes.push({
          group: group, type: 'specimenAliquot', title: 'specimens.spmn_aliquots',
          showImportType: false, importType    : 'CREATE'
        });
        importTypes.push({
          group: group, type: 'specimenDerivative', title: 'specimens.spmn_derivatives',
          showImportType: false, importType    : 'CREATE'
        });
      }

      if (!cp.specimenCentric) {
        importTypes.push({
          group: group, type: 'masterSpecimen', title: 'participant.master_specimens',
          showImportType: false, importType    : 'CREATE', showUpsert: true
        });
      }

      if (cp.id == -1) {
        importTypes.push({
          group: group, type: 'containerSpecimen', title: 'specimens.container_specimens',
          showImportType: false, importType: 'UPDATE'
        });
      }

      importTypes.push({
        group: group, type: 'specimenDisposal', title: 'participant.specimen_disposal',
        showImportType: false, importType    : 'UPDATE'
      });

      addPluginTypes(importTypes, group, 'Specimen');
      addForms(importTypes, group, 'Specimen', entityForms['Specimen']);
      addForms(importTypes, group, 'SpecimenEvent', entityForms['SpecimenEvent']);
      return addEventTypes(cp, importTypes, group);
    }

    function addEventTypes(cp, importTypes, group) {
      if (cp.id != -1) {
        return importTypes;
      }

      var events = [
        'containerTransferEvent', 'specimenDisposalEvent',
        'specimenReservedEvent', 'specimenReservationCancelEvent',
        'specimenReturnEvent', 'specimenTransferEvent'
      ];

      angular.forEach(events,
        function(event) {
          importTypes.push({
            group: group,
            type: event,
            title: 'bulk_imports.object_types.' + event,
            showImportType: false,
            importType: 'UPDATE',
            params: {}
          });
        }
      );

      return importTypes;
    }

    function getImportDetail(cp, allowedEntityTypes, forms) {
      var breadcrumbs, onSuccess;
      if (cp.id == -1) {
        breadcrumbs = [{state: 'cp-list', title: "cp.list"}];
        onSuccess = {state: 'import-multi-cp-jobs'};
      } else {
        breadcrumbs = [{state: 'cp-list-view', title: cp.shortTitle, params: '{cpId:' + cp.id + '}'}];
        onSuccess = {state: 'import-cp-jobs', params: {cpId: cp.id}};
      }

      var entityForms = {};
      angular.forEach(forms, function(form) {
        if (!entityForms[form.entityType]) {
          entityForms[form.entityType] = [];
        }

        entityForms[form.entityType].push(form);
      });

      var importTypes = [];
      if (!cp.specimenCentric && allowedEntityTypes.indexOf('Participant') >= 0) {
        importTypes = importTypes.concat(getParticipantTypes(entityForms, cp, allowedEntityTypes.indexOf('Consent') >= 0));
      } else if (!cp.specimenCentric && allowedEntityTypes.indexOf('Consent') >= 0) {
        importTypes = importTypes.concat(getConsentTypes(cp, true));
      }

      if (!cp.specimenCentric && allowedEntityTypes.indexOf('SpecimenCollectionGroup') >= 0) {
        importTypes = importTypes.concat(getVisitTypes(entityForms, cp, allowedEntityTypes.indexOf('Consent') >= 0));
      }

      if (allowedEntityTypes.indexOf('Specimen') >= 0) {
        importTypes = importTypes.concat(getSpecimenTypes(cp, allowedEntityTypes, entityForms));
      }

      angular.forEach(importTypes,
        function(importType) {
          if (!importType.params) {
            importType.params = {};
          }

          importType.params.cpId = cp.id;
        }
      );

      return {
        breadcrumbs: breadcrumbs,
        title: 'Bulk Import Records',
        objectType: undefined,
        onSuccess: onSuccess,
        types: importTypes,
        objectParams: {
          cpId: cp.id
        }
      };
    }

    return {
      getImportDetail: function(cp, allowedEntityTypes, forms) {
        return $translate('common.none').then(
          function() {
            return getImportDetail(cp, allowedEntityTypes, forms);
          }
        );
      },

      registerTypes: function(entityType, types) {
        if (!types) {
          delete pluginTypes[entityType];
        } else {
          pluginTypes[entityType] = types;
        }
      },

      getPluginTypes: function() {
        var result = [];
        angular.forEach(pluginTypes,
          function(types, key) {
            angular.forEach(types,
              function(type) {
                result.push(type.type);
              }
            );
          }
        );

        return result;
      }
    }
  });
<|endoftext|>


<commit_before>// __BEGIN_LICENSE__
//Copyright (c) 2015, United States Government, as represented by the
//Administrator of the National Aeronautics and Space Administration.
//All rights reserved.
//
//The xGDS platform is licensed under the Apache License, Version 2.0
//(the "License"); you may not use this file except in compliance with the License.
//You may obtain a copy of the License at
//http://www.apache.org/licenses/LICENSE-2.0.
//
//Unless required by applicable law or agreed to in writing, software distributed
//under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
//CONDITIONS OF ANY KIND, either express or implied. See the License for the
//specific language governing permissions and limitations under the License.
// __END_LICENSE__

/**
 * Create a slider ribbon that shows breaks between segments
 */
function createSliderLegend(isResizing) {
    var ribbon = $("#ribbon");
    if (_.isUndefined(xgds_video.resizeSlider)){
        $( window ).resize(function() {
            createSliderLegend(true);
        });
        xgds_video.resizeSlider = true;
    }

     for (var sourceName in xgds_video.displaySegments) {
         var segments = xgds_video.displaySegments[sourceName];
         var source = segments[0].source;
         var dividerName = "divider-" + source.shortName;
         if (isResizing){
             $("#ribbon").find("#"+dividerName).remove();
         }
        var labels = {};
        var endPointCheck = true;
        $.each(segments, function(id) {
            var segment = segments[id];
            if (!segment.endTime) {
                endPointCheck = false;
                return false;
            }
        });
        if (!endPointCheck) {
            return;
        }
        //get the total slider range in seconds
        var episodeStartMoment = moment(xgds_video.episode.startTime);
        var episodeEndMoment = moment(xgds_video.episode.endTime);
        var totalDuration = episodeEndMoment.diff(episodeStartMoment, 'seconds');
        var color = source.displayColor;
        if (color == '') {
            //assign a random color
            color = '#' + (Math.random() * 0xFFFFFF << 0).toString(16);
        }
        //handle empty space in front of first segment
        var firstSegmentStartMoment = moment(segments[0].startTime);
        var emptySegmentDuration = firstSegmentStartMoment.diff(episodeStartMoment, 'seconds');
        var fullWidth = $("#masterSlider").width();
        var emptySegmentWidth = Math.round(fullWidth * (emptySegmentDuration / totalDuration));
        var emptySegmentHTML = '';
        ribbon.append('
' + source.shortName );
        sourceRibbon = ribbon.find("#divider-" + source.shortName);
        sourceRibbon.append(emptySegmentHTML);
        //for each video segment
        $.each(segments, function(id) {
            var segment = segments[id];
            var source = segment.source;
            var segDuration = 0;
            var width = 0;
            //get the duration of the video segment
            var segmentStartMoment = moment(segment.startTime);
            var segmentEndMoment = moment(segment.endTime);
            segDuration = segmentEndMoment.diff(segmentStartMoment, 'seconds');
            width = fullWidth * (segDuration / totalDuration);
            width = Math.round(width);
            //draw the visualization
            var htmlNugget = '';
            sourceRibbon.append(htmlNugget);
            if ((id + 1) < segments.length) { //if there is a next segment
                var nextSegment = segments[id + 1];
                var nextSegmentStartMoment = moment(nextSegment.startTime);
                var gapTime = nextSegmentStartMoment.diff(segmentEndMoment, 'seconds');
                emptySegmentWidth = Math.round(fullWidth * (gapTime / totalDuration));
                var htmlNugget2 = '';
                sourceRibbon.append(htmlNugget2);
            }
        });
    }
}

function getPercent(width, totalWidth) {
    return Math.round(width / totalWidth * 100);
}

/**
 * Slider Callback:
 * update slider time text when moving slider.
 */
function uponSliderMoveCallBack(event, ui) {
    //update slider time label on top
    xgds_video.movingSlider = true;
    var sliderTime = new Date(ui.value * 1000);
    setSliderTimeLabel(sliderTime);
}

/**
 * Slider Callback:
 *    get the current slider position and do
 *    offset = slider position - each video's start time
 *    seek each video at offset. (means each video's offset will be different,
 *    but their test site time same)
 *    update the test site times to equal slider position.
 */
function uponSliderStopCallBack(event, ui) {
    xgds_video.seekFlag = true;
    var currTime = xgds_video.masterSlider.slider('value'); //in seconds
    currTime = new Date(currTime * 1000); //convert to javascript date
    for (var source in xgds_video.displaySegments) {
        jumpToPosition(currTime, source); //sourceName);
        //XXX take care of the case where seek time is not within playable range.
        //then go to the nearest available segment and play from there.
    }
    xgds_video.movingSlider = false;
}

/**
 * initialize master slider with range (episode start time->episode end time)
 */
function setupSlider() {
    if (Object.keys(xgds_video.displaySegments).length == 0){
        return;
    }
    var endTime = xgds_video.episode.endTime ? xgds_video.episode.endTime :
                    xgds_video.lastSegment.endTime;
    var endMoment = moment(endTime);
    var startMoment = moment(xgds_video.episode.startTime);
    var duration = endMoment.diff(startMoment, 'seconds')
    if (xgds_video.episode.endTime) {
        xgds_video.masterSlider = $('#masterSlider').slider({
            step: 1,
            min: moment(xgds_video.firstSegment.startTime).unix(),
            max: endMoment.unix(),
            stop: uponSliderStopCallBack,
            slide: uponSliderMoveCallBack,
            range: 'min'
        });
        setSliderTimeLabel(xgds_video.episode.startTime);
        createSliderLegend(false);
    } else {
        alert('The end time of video segment not available.' +
        'Cannot setup slider');
    }
}
<commit_msg>UNTESTED trying to fix slider width for single source video viewing<commit_after>// __BEGIN_LICENSE__
//Copyright (c) 2015, United States Government, as represented by the
//Administrator of the National Aeronautics and Space Administration.
//All rights reserved.
//
//The xGDS platform is licensed under the Apache License, Version 2.0
//(the "License"); you may not use this file except in compliance with the License.
//You may obtain a copy of the License at
//http://www.apache.org/licenses/LICENSE-2.0.
//
//Unless required by applicable law or agreed to in writing, software distributed
//under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
//CONDITIONS OF ANY KIND, either express or implied. See the License for the
//specific language governing permissions and limitations under the License.
// __END_LICENSE__

/**
 * Create a slider ribbon that shows breaks between segments
 */
function createSliderLegend(isResizing) {
    var ribbon = $("#ribbon");
    if (_.isUndefined(xgds_video.resizeSlider)){
        $( window ).resize(function() {
            createSliderLegend(true);
        });
        xgds_video.resizeSlider = true;
    }

    var singleSource = (Object.keys(xgds_video.displaySegments).length == 1);
     for (var sourceName in xgds_video.displaySegments) {
         var segments = xgds_video.displaySegments[sourceName];
         var source = segments[0].source;
         var dividerName = "divider-" + source.shortName;
         if (isResizing){
             $("#ribbon").find("#"+dividerName).remove();
         }
        var labels = {};
        var endPointCheck = true;
        $.each(segments, function(id) {
            var segment = segments[id];
            if (!segment.endTime) {
                endPointCheck = false;
                return false;
            }
        });
        if (!endPointCheck) {
            return;
        }
        //get the total slider range in seconds
        var firstSegmentStartMoment = moment(segments[0].startTime);
        var episodeStartMoment = singleSource ? firstSegmentStartMoment : moment(xgds_video.episode.startTime);
        var episodeEndMoment = singleSource ? moment(segments[segments.length - 1].endTime) : moment(xgds_video.episode.endTime);
        var totalDuration = episodeEndMoment.diff(episodeStartMoment, 'seconds');
        var color = source.displayColor;
        if (color == '') {
            //assign a random color
            color = '#' + (Math.random() * 0xFFFFFF << 0).toString(16);
        }

        sourceRibbon = ribbon.find("#divider-" + source.shortName);

        if (!singleSource){
            //handle empty space in front of first segment
            var emptySegmentDuration = firstSegmentStartMoment.diff(episodeStartMoment, 'seconds');
            var fullWidth = $("#masterSlider").width();
            var emptySegmentWidth = Math.round(fullWidth * (emptySegmentDuration / totalDuration));
            var emptySegmentHTML = '';
            ribbon.append('
' + source.shortName );
            sourceRibbon.append(emptySegmentHTML);
        }
        //for each video segment
        $.each(segments, function(id) {
            var segment = segments[id];
            var source = segment.source;
            var segDuration = 0;
            var width = 0;
            //get the duration of the video segment
            var segmentStartMoment = moment(segment.startTime);
            var segmentEndMoment = moment(segment.endTime);
            segDuration = segmentEndMoment.diff(segmentStartMoment, 'seconds');
            width = fullWidth * (segDuration / totalDuration);
            width = Math.round(width);
            //draw the visualization
            var htmlNugget = '';
            sourceRibbon.append(htmlNugget);
            if ((id + 1) < segments.length) { //if there is a next segment
                var nextSegment = segments[id + 1];
                var nextSegmentStartMoment = moment(nextSegment.startTime);
                var gapTime = nextSegmentStartMoment.diff(segmentEndMoment, 'seconds');
                emptySegmentWidth = Math.round(fullWidth * (gapTime / totalDuration));
                var htmlNugget2 = '';
                sourceRibbon.append(htmlNugget2);
            }
        });
    }
}

function getPercent(width, totalWidth) {
    return Math.round(width / totalWidth * 100);
}

/**
 * Slider Callback:
 * update slider time text when moving slider.
 */
function uponSliderMoveCallBack(event, ui) {
    //update slider time label on top
    xgds_video.movingSlider = true;
    var sliderTime = new Date(ui.value * 1000);
    setSliderTimeLabel(sliderTime);
}

/**
 * Slider Callback:
 *    get the current slider position and do
 *    offset = slider position - each video's start time
 *    seek each video at offset. (means each video's offset will be different,
 *    but their test site time same)
 *    update the test site times to equal slider position.
 */
function uponSliderStopCallBack(event, ui) {
    xgds_video.seekFlag = true;
    var currTime = xgds_video.masterSlider.slider('value'); //in seconds
    currTime = new Date(currTime * 1000); //convert to javascript date
    for (var source in xgds_video.displaySegments) {
        jumpToPosition(currTime, source); //sourceName);
        //XXX take care of the case where seek time is not within playable range.
        //then go to the nearest available segment and play from there.
    }
    xgds_video.movingSlider = false;
}

/**
 * initialize master slider with range (episode start time->episode end time)
 */
function setupSlider() {
    if (Object.keys(xgds_video.displaySegments).length == 0){
        return;
    }
    var endTime = xgds_video.episode.endTime ? xgds_video.episode.endTime :
                    xgds_video.lastSegment.endTime;
    var endMoment = moment(endTime);
    var startMoment = moment(xgds_video.episode.startTime);
    var duration = endMoment.diff(startMoment, 'seconds')
    if (xgds_video.episode.endTime) {
        xgds_video.masterSlider = $('#masterSlider').slider({
            step: 1,
            min: moment(xgds_video.firstSegment.startTime).unix(),
            max: endMoment.unix(),
            stop: uponSliderStopCallBack,
            slide: uponSliderMoveCallBack,
            range: 'min'
        });
        setSliderTimeLabel(xgds_video.episode.startTime);
        createSliderLegend(false);
    } else {
        alert('The end time of video segment not available.' +
        'Cannot setup slider');
    }
}
<|endoftext|>


<commit_before>/*  ============================================================================

 Copyright (C) 2006-2018 Talend Inc. - www.talend.com

 This source code is available under agreement available at
 https://github.com/Talend/data-prep/blob/master/LICENSE

 You should have received a copy of the agreement
 along with this program; if not, write to Talend SA
 9 rue Pages 92150 Suresnes, France

 ============================================================================*/

import _ from 'lodash';

/**
 * @ngdoc service
 * @name data-prep.services.playground.service:DatagridService
 * @description Datagrid service. This service holds the datagrid (SlickGrid) view and the (SlickGrid) filters

 * WARNING : do NOT use this service directly for FILTERS.
 * {@link data-prep.services.filter.service:FilterService FilterService} must be the only entry point for datagrid filters
 * @requires data-prep.services.state.constant:state
 * @requires data-prep.services.state.service:StateService
 * @requires data-prep.services.utils.service:ConverterService
 * @requires data-prep.services.utils.service:TextFormatService
 */
export default function DatagridService(state, StateService, ConverterService, TextFormatService) {
	'ngInject';

	const DELETE = 'DELETE';
	const REPLACE = 'REPLACE';
	const INSERT = 'INSERT';

	const service = {
		focusedColumn: null, // TODO JSO : put this in state

		// grid data
		updateData, // updata data in the current dataset
		getColumns,
		getColumnsContaining,

		// preview
		execute,
		previewDataExecutor,
	};

	return service;

    //------------------------------------------------------------------------------------------------------
    // ---------------------------------------------------DATA-----------------------------------------------
    //------------------------------------------------------------------------------------------------------
    /**
     * @ngdoc method
     * @name getLastNewColumnId
     * @methodOf data-prep.services.playground.service:DatagridService
     * @param {object} columns The new columns
     * @description Get the last new created column
     */
	function getLastNewColumnId(columns) {
		const ancientColumnsIds = _.map(state.playground.data.metadata.columns, 'id');
		const newColumnsIds = _.map(columns, 'id');
		const diffIds = _.difference(newColumnsIds, ancientColumnsIds);

		return diffIds[diffIds.length - 1];
	}

    /**
     * @ngdoc method
     * @name updateData
     * @methodOf data-prep.services.playground.service:DatagridService
     * @param {Object} data - the new data (columns and records)
     * @description Update the data in the datagrid
     */
	function updateData(data) {
		if (state.playground.data.metadata.columns.length < data.metadata.columns.length) {
			service.focusedColumn = getLastNewColumnId(data.metadata.columns);
		}

		StateService.setCurrentData(data);
	}

    //------------------------------------------------------------------------------------------------------
    // --------------------------------------------------PREVIEW---------------------------------------------
    //------------------------------------------------------------------------------------------------------
    /**
     * @ngdoc method
     * @name execute
     * @methodOf data-prep.services.playground.service:DatagridService
     * @param {Object} executor The info to apply on the dataset
     * @description Update the data in the datagrid with a set of instructions and the column list to apply.
     * This allows to update the dataset, with limited SlickGrid computation, for more performant operations than
     * setItems which compute everything on the whole dataset.
     */
	function execute(executor) {
		if (!executor) {
			return;
		}

		const revertInstructions = [];

		state.playground.grid.dataView.beginUpdate();
		_.forEach(executor.instructions, function (step) {
			switch (step.type) {
			case INSERT:
				state.playground.grid.dataView.insertItem(step.index, step.row);
				revertInstructions.push({
					type: DELETE,
					row: step.row,
				});
				break;
			case DELETE: {
				const index = state.playground.grid.dataView.getIdxById(step.row.tdpId);
				state.playground.grid.dataView.deleteItem(step.row.tdpId);
				revertInstructions.push({
					type: INSERT,
					row: step.row,
					index,
				});
				break;
			}
			case REPLACE: {
				const originalRow = state.playground.grid.dataView.getItemById(step.row.tdpId);
				state.playground.grid.dataView.updateItem(step.row.tdpId, step.row);
				revertInstructions.push({
					type: REPLACE,
					row: originalRow,
				});
				break;
			}
			}
		});

		state.playground.grid.dataView.endUpdate();

		const reverter = {
			instructions: revertInstructions,
			preview: state.playground.data.preview,
			metadata: state.playground.data.metadata,
		};

		if (state.playground.data.metadata.columns.length < executor.metadata.columns.length) {
			service.focusedColumn = getLastNewColumnId(executor.metadata.columns);
		}

		StateService.setCurrentData({
			metadata: executor.metadata,
			records: state.playground.data.records,
			preview: executor.preview,
		});

		return reverter;
	}

    /**
     * @ngdoc method
     * @name previewDataExecutor
     * @methodOf data-prep.services.playground.service:DatagridService
     * @param {Object} data The new preview data to insert
     * @description Create an executor that reflect the provided preview data, in order to update the current dataset
     */
	function previewDataExecutor(data) {
		const executor = {
			metadata: data.metadata,
			instructions: [],
			preview: true,
		};

		let nextInsertionIndex = state.playground.grid.dataView.getIdxById(data.records[0].tdpId);
		_.forEach(data.records, function (row) {
			if (row.__tdpRowDiff || row.__tdpDiff) { // eslint-disable-line no-underscore-dangle
				if (row.__tdpRowDiff === 'new') { // eslint-disable-line no-underscore-dangle
					executor.instructions.push({
						type: INSERT,
						row,
						index: nextInsertionIndex,
					});
				}
				else {
					executor.instructions.push({
						type: REPLACE,
						row,
					});
				}
			}

			nextInsertionIndex++;
		});

		return executor;
	}

    //------------------------------------------------------------------------------------------------------
    // ------------------------------------------------DATA UTILS--------------------------------------------
    //------------------------------------------------------------------------------------------------------
    /**
     * @ngdoc method
     * @name getColumns
     * @methodOf data-prep.services.playground.service:DatagridService
     * @param {boolean} excludeNumeric - filter the numeric columns
     * @param {boolean} excludeBoolean - filter the boolean columns
     * @description Filter the column ids
     * @returns {Object[]} - the column list that match the desired filters (id & name)
     */
	function getColumns(excludeNumeric, excludeBoolean) {
		let cols = state.playground.data.metadata.columns;

		if (excludeNumeric) {
			cols = _.filter(cols, (col) => {
				const simplifiedType = ConverterService.simplifyType(col.type);
				return simplifiedType !== 'integer' && simplifiedType !== 'decimal';
			});
		}

		if (excludeBoolean) {
			cols = _.filter(cols, col => col.type !== 'boolean');
		}

		return _.map(cols, col => ({ id: col.id, name: col.name }));
	}

    /**
     * @ngdoc method
     * @name getColumnsContaining
     * @methodOf data-prep.services.playground.service:DatagridService
     * @description Return the column id list that has a value that match the regexp
     * @returns {Object[]} The column list that contains a value that match the regexp (col.id & col.name)
     */
	function getColumnsContaining(phrase) {
		const results = [];

		if (!phrase) {
			return results;
		}

		const regexp = new RegExp(TextFormatService.escapeRegexpExceptStar(phrase));
		const canBeNumeric = !isNaN(phrase.replace(/\*/g, ''));
		const canBeBoolean = 'true'.match(regexp) || 'false'.match(regexp);

		const data = state.playground.data.records;
		let potentialColumns = getColumns(!canBeNumeric, !canBeBoolean);

		// we loop over data while there is data and potential columns that can contains the searched term
		// if a col value for a row contains the term, we add it to result
		let dataIndex = 0;
		while (dataIndex < data.length && potentialColumns.length) {
			const record = data[dataIndex];

			for (const col of potentialColumns) {
				if (record[col.id].toLowerCase().match(regexp)) {
					potentialColumns = potentialColumns.filter(nextCol => nextCol !== col);
					results.push(col);
				}
			}

			potentialColumns = _.difference(potentialColumns, results);
			dataIndex++;
		}

		return results;
	}
}
<commit_msg>fix(TDP-5883): searching for an alphanumeric value in an integer column doesnt work (#1702)<commit_after>/*  ============================================================================

 Copyright (C) 2006-2018 Talend Inc. - www.talend.com

 This source code is available under agreement available at
 https://github.com/Talend/data-prep/blob/master/LICENSE

 You should have received a copy of the agreement
 along with this program; if not, write to Talend SA
 9 rue Pages 92150 Suresnes, France

 ============================================================================*/

import _ from 'lodash';

/**
 * @ngdoc service
 * @name data-prep.services.playground.service:DatagridService
 * @description Datagrid service. This service holds the datagrid (SlickGrid) view and the (SlickGrid) filters

 * WARNING : do NOT use this service directly for FILTERS.
 * {@link data-prep.services.filter.service:FilterService FilterService} must be the only entry point for datagrid filters
 * @requires data-prep.services.state.constant:state
 * @requires data-prep.services.state.service:StateService
 * @requires data-prep.services.utils.service:ConverterService
 * @requires data-prep.services.utils.service:TextFormatService
 */
export default function DatagridService(state, StateService, ConverterService, TextFormatService) {
	'ngInject';

	const DELETE = 'DELETE';
	const REPLACE = 'REPLACE';
	const INSERT = 'INSERT';

	const service = {
		focusedColumn: null, // TODO JSO : put this in state

		// grid data
		updateData, // updata data in the current dataset
		getColumns,
		getColumnsContaining,

		// preview
		execute,
		previewDataExecutor,
	};

	return service;

    //------------------------------------------------------------------------------------------------------
    // ---------------------------------------------------DATA-----------------------------------------------
    //------------------------------------------------------------------------------------------------------
    /**
     * @ngdoc method
     * @name getLastNewColumnId
     * @methodOf data-prep.services.playground.service:DatagridService
     * @param {object} columns The new columns
     * @description Get the last new created column
     */
	function getLastNewColumnId(columns) {
		const ancientColumnsIds = _.map(state.playground.data.metadata.columns, 'id');
		const newColumnsIds = _.map(columns, 'id');
		const diffIds = _.difference(newColumnsIds, ancientColumnsIds);

		return diffIds[diffIds.length - 1];
	}

    /**
     * @ngdoc method
     * @name updateData
     * @methodOf data-prep.services.playground.service:DatagridService
     * @param {Object} data - the new data (columns and records)
     * @description Update the data in the datagrid
     */
	function updateData(data) {
		if (state.playground.data.metadata.columns.length < data.metadata.columns.length) {
			service.focusedColumn = getLastNewColumnId(data.metadata.columns);
		}

		StateService.setCurrentData(data);
	}

    //------------------------------------------------------------------------------------------------------
    // --------------------------------------------------PREVIEW---------------------------------------------
    //------------------------------------------------------------------------------------------------------
    /**
     * @ngdoc method
     * @name execute
     * @methodOf data-prep.services.playground.service:DatagridService
     * @param {Object} executor The info to apply on the dataset
     * @description Update the data in the datagrid with a set of instructions and the column list to apply.
     * This allows to update the dataset, with limited SlickGrid computation, for more performant operations than
     * setItems which compute everything on the whole dataset.
     */
	function execute(executor) {
		if (!executor) {
			return;
		}

		const revertInstructions = [];

		state.playground.grid.dataView.beginUpdate();
		_.forEach(executor.instructions, function (step) {
			switch (step.type) {
			case INSERT:
				state.playground.grid.dataView.insertItem(step.index, step.row);
				revertInstructions.push({
					type: DELETE,
					row: step.row,
				});
				break;
			case DELETE: {
				const index = state.playground.grid.dataView.getIdxById(step.row.tdpId);
				state.playground.grid.dataView.deleteItem(step.row.tdpId);
				revertInstructions.push({
					type: INSERT,
					row: step.row,
					index,
				});
				break;
			}
			case REPLACE: {
				const originalRow = state.playground.grid.dataView.getItemById(step.row.tdpId);
				state.playground.grid.dataView.updateItem(step.row.tdpId, step.row);
				revertInstructions.push({
					type: REPLACE,
					row: originalRow,
				});
				break;
			}
			}
		});

		state.playground.grid.dataView.endUpdate();

		const reverter = {
			instructions: revertInstructions,
			preview: state.playground.data.preview,
			metadata: state.playground.data.metadata,
		};

		if (state.playground.data.metadata.columns.length < executor.metadata.columns.length) {
			service.focusedColumn = getLastNewColumnId(executor.metadata.columns);
		}

		StateService.setCurrentData({
			metadata: executor.metadata,
			records: state.playground.data.records,
			preview: executor.preview,
		});

		return reverter;
	}

    /**
     * @ngdoc method
     * @name previewDataExecutor
     * @methodOf data-prep.services.playground.service:DatagridService
     * @param {Object} data The new preview data to insert
     * @description Create an executor that reflect the provided preview data, in order to update the current dataset
     */
	function previewDataExecutor(data) {
		const executor = {
			metadata: data.metadata,
			instructions: [],
			preview: true,
		};

		let nextInsertionIndex = state.playground.grid.dataView.getIdxById(data.records[0].tdpId);
		_.forEach(data.records, function (row) {
			if (row.__tdpRowDiff || row.__tdpDiff) { // eslint-disable-line no-underscore-dangle
				if (row.__tdpRowDiff === 'new') { // eslint-disable-line no-underscore-dangle
					executor.instructions.push({
						type: INSERT,
						row,
						index: nextInsertionIndex,
					});
				}
				else {
					executor.instructions.push({
						type: REPLACE,
						row,
					});
				}
			}

			nextInsertionIndex++;
		});

		return executor;
	}

    //------------------------------------------------------------------------------------------------------
    // ------------------------------------------------DATA UTILS--------------------------------------------
    //------------------------------------------------------------------------------------------------------
    /**
     * @ngdoc method
     * @name getColumns
     * @methodOf data-prep.services.playground.service:DatagridService
     * @param {boolean} excludeNumeric - filter the numeric columns
     * @param {boolean} excludeBoolean - filter the boolean columns
     * @description Filter the column ids
     * @returns {Object[]} - the column list that match the desired filters (id & name)
     */
	function getColumns(excludeNumeric, excludeBoolean) {
		let cols = state.playground.data.metadata.columns;

		if (excludeNumeric) {
			cols = _.filter(cols, (col) => {
				const simplifiedType = ConverterService.simplifyType(col.type);
				return simplifiedType !== 'integer' && simplifiedType !== 'decimal';
			});
		}

		if (excludeBoolean) {
			cols = _.filter(cols, col => col.type !== 'boolean');
		}

		return _.map(cols, col => ({ id: col.id, name: col.name }));
	}

    /**
     * @ngdoc method
     * @name getColumnsContaining
     * @methodOf data-prep.services.playground.service:DatagridService
     * @description Return the column id list that has a value that match the regexp
     * @returns {Object[]} The column list that contains a value that match the regexp (col.id & col.name)
     */
	function getColumnsContaining(phrase) {
		const results = [];

		if (!phrase) {
			return results;
		}

		const regexp = new RegExp(TextFormatService.escapeRegexpExceptStar(phrase));
		const data = state.playground.data.records;
		let potentialColumns = getColumns();

		// we loop over data while there is data and potential columns that can contains the searched term
		// if a col value for a row contains the term, we add it to result
		let dataIndex = 0;
		while (dataIndex < data.length && potentialColumns.length) {
			const record = data[dataIndex];

			for (const col of potentialColumns) {
				if (record[col.id].toLowerCase().match(regexp)) {
					potentialColumns = potentialColumns.filter(nextCol => nextCol !== col);
					results.push(col);
				}
			}

			potentialColumns = _.difference(potentialColumns, results);
			dataIndex++;
		}

		return results;
	}
}
<|endoftext|>


<commit_before>/**
 * JavaScript SwatCheckboxList component
 *
 * @param id string Id of the matching {@link SwatCheckboxList} object.
 */
function SwatCheckboxList(id)
{
	this.check_list = document.getElementsByName(id + '[]');
	this.check_all = null; // a reference to a check-all js object

	for (i = 0; i < this.check_list.length; i++) {
		YAHOO.util.Event.addListener(this.check_list[i], 'change',
			SwatCheckboxList.clickHandler, this);
	}
}

SwatCheckboxList.clickHandler = function(event, object)
{
	object.checkAllInit();
}

SwatCheckboxList.prototype.checkAllInit = function ()
{
	if (this.check_all == null)
		return;

	var count = 0;
	for (i = 0; i < this.check_list.length; i++)
		if (this.check_list[i].checked)
			count++;
		else if (count > 0)
			break; // can't possibly be all checked or none checked

	this.check_all.setState(count == this.check_list.length);
}

SwatCheckboxList.prototype.checkAll = function(checked)
{
	for (i = 0; i < this.check_list.length; i++)
		this.check_list[i].checked = checked;
}
<commit_msg>Use click event instead of change event for checkbox list so it works in IE<commit_after>/**
 * JavaScript SwatCheckboxList component
 *
 * @param id string Id of the matching {@link SwatCheckboxList} object.
 */
function SwatCheckboxList(id)
{
	this.check_list = document.getElementsByName(id + '[]');
	this.check_all = null; // a reference to a check-all js object

	for (i = 0; i < this.check_list.length; i++) {
		YAHOO.util.Event.addListener(this.check_list[i], 'click',
			SwatCheckboxList.clickHandler, this);
	}
}

SwatCheckboxList.clickHandler = function(event, object)
{
	object.checkAllInit();
}

SwatCheckboxList.prototype.checkAllInit = function ()
{
	if (this.check_all == null)
		return;

	var count = 0;
	for (i = 0; i < this.check_list.length; i++)
		if (this.check_list[i].checked)
			count++;
		else if (count > 0)
			break; // can't possibly be all checked or none checked

	this.check_all.setState(count == this.check_list.length);
}

SwatCheckboxList.prototype.checkAll = function(checked)
{
	for (i = 0; i < this.check_list.length; i++)
		this.check_list[i].checked = checked;
}
<|endoftext|>


<commit_before>function WalletSendAssetsController($modal, $scope, $http, $q, userService, walletTransactionService) {
  $scope.walletAssets =  $scope.$parent.$parent;
  var transactionGenerationController = $scope.$parent;

  transactionGenerationController.validateTransactionData = function(){
    var dustValue = 5757;
    var minerMinimum = 10000;
    var nonZeroValue = 1;
    var divisible = $scope.selectedCoin.divisible;

    var convertToSatoshi = [
        $scope.minerFees,
        $scope.sendAmount,
        $scope.balanceData[0],
        $scope.balanceData[1],
        $scope.totalCost
      ];

    if (!divisible) {
      delete convertToSatoshi[ convertToSatoshi.indexOf( $scope.sendAmount ) ];
      delete convertToSatoshi[ convertToSatoshi.indexOf( $scope.balanceData[0] ) ];
    }

    var convertedValues = $scope.convertDisplayedValue( convertToSatoshi );

    var minerFees = +convertedValues[0];
    var sendAmount = divisible ? +convertedValues[1] : +$scope.sendAmount;
    var totalFeeCost = +convertedValues[4];

    var balance = divisible ? +convertedValues[2] : +$scope.balanceData[0];
    var btcbalance = +convertedValues[3];

    var coin = $scope.selectedCoin.symbol;
    var address = $scope.selectedAddress;
    var sendTo = $scope.sendTo;
    var marked = $scope.marked;

    var required = [coin, address, sendAmount, sendTo, minerFees, totalFeeCost,  balance, btcbalance, $scope.sendForm.$valid, marked];

    var error = 'Please ';
    if ($scope.sendForm.$valid == false) {
      error += 'make sure all fields are completely filled, ';
    }
    if ((sendAmount <= balance) == false) {
      error += 'make sure you aren\'t sending more tokens than you own, ';
    }
    if ((totalFeeCost <= btcbalance) == false) {
      error += 'make sure you have enough Bitcoin to cover your transaction costs, ';
    }
    if (walletTransactionService.validAddress(sendTo) == false) {
      error += 'make sure you are sending to a valid MSC/BTC address, ';
    }
    if (coin == 'BTC') {
      if (sendAmount < dustValue)
        error += 'make sure your send amount is at least 0.00005430 BTC if sending BTC, ';
      if (minerFees < minerMinimum)
        error += 'make sure your fee entry is at least 0.0001 BTC to cover miner costs, ';
    }
    if( ((coin == 'MSC') || (coin == 'TMSC')) ) {
      if (sendAmount < nonZeroValue)
        error += 'make sure your send amount is non-zero, ';
      if (minerFees < minerMinimum)
        error += 'make sure your fee entry is at least 0.0001 BTC, ';
    }

    return error;
  };

  transactionGenerationController.modalTemplateUrl = '/partials/wallet_send_modal.html';

  transactionGenerationController.setModalScope = function($modalScope){
    $modalScope.transactionSuccess = false, $modalScope.transactionError = false, $modalScope.waiting = false, $modalScope.privKeyPass = {};
    $modalScope.convertSatoshiToDisplayedValue=  $scope.convertSatoshiToDisplayedValue,
    $modalScope.getDisplayedAbbreviation=  $scope.getDisplayedAbbreviation,
    $modalScope.sendAmount=  $scope.selectedCoin.divisible ? +$scope.convertDisplayedValue($scope.sendAmount) : +$scope.sendAmount,
    $modalScope.minerFees= +$scope.convertDisplayedValue($scope.minerFees),
    $modalScope.sendTo= $scope.sendTo;
    $modalScope.totalCost= +$scope.convertDisplayedValue($scope.totalCost);
  };

  transactionGenerationController.generateData = function(){
    return {
      from:$scope.selectedAddress,
      transactionType:0,
      transactionData:{
        transaction_version:0,
        transaction_from: $scope.selectedAddress,
        currency_identifier:$scope.selectedCoin.id,
        amount_to_transfer : $scope.selectedCoin.divisible ? +$scope.convertDisplayedValue($scope.sendAmount) : +$scope.sendAmount,
        transaction_to: $scope.sendTo,
        fee: $scope.convertDisplayedValue($scope.minerFees),
        marker: $scope.marked
      }
    };
  };
};

<commit_msg>Fix info about minimum send amount<commit_after>function WalletSendAssetsController($modal, $scope, $http, $q, userService, walletTransactionService) {
  $scope.walletAssets =  $scope.$parent.$parent;
  var transactionGenerationController = $scope.$parent;

  transactionGenerationController.validateTransactionData = function(){
    var dustValue = 5757;
    var minerMinimum = 10000;
    var nonZeroValue = 1;
    var divisible = $scope.selectedCoin.divisible;

    var convertToSatoshi = [
        $scope.minerFees,
        $scope.sendAmount,
        $scope.balanceData[0],
        $scope.balanceData[1],
        $scope.totalCost
      ];

    if (!divisible) {
      delete convertToSatoshi[ convertToSatoshi.indexOf( $scope.sendAmount ) ];
      delete convertToSatoshi[ convertToSatoshi.indexOf( $scope.balanceData[0] ) ];
    }

    var convertedValues = $scope.convertDisplayedValue( convertToSatoshi );

    var minerFees = +convertedValues[0];
    var sendAmount = divisible ? +convertedValues[1] : +$scope.sendAmount;
    var totalFeeCost = +convertedValues[4];

    var balance = divisible ? +convertedValues[2] : +$scope.balanceData[0];
    var btcbalance = +convertedValues[3];

    var coin = $scope.selectedCoin.symbol;
    var address = $scope.selectedAddress;
    var sendTo = $scope.sendTo;
    var marked = $scope.marked;

    var required = [coin, address, sendAmount, sendTo, minerFees, totalFeeCost,  balance, btcbalance, $scope.sendForm.$valid, marked];

    var error = 'Please ';
    if ($scope.sendForm.$valid == false) {
      error += 'make sure all fields are completely filled, ';
    }
    if ((sendAmount <= balance) == false) {
      error += 'make sure you aren\'t sending more tokens than you own, ';
    }
    if ((totalFeeCost <= btcbalance) == false) {
      error += 'make sure you have enough Bitcoin to cover your transaction costs, ';
    }
    if (walletTransactionService.validAddress(sendTo) == false) {
      error += 'make sure you are sending to a valid MSC/BTC address, ';
    }
    if (coin == 'BTC') {
      if (sendAmount < dustValue)
        error += 'make sure your send amount is at least 0.00005757 BTC if sending BTC, ';
      if (minerFees < minerMinimum)
        error += 'make sure your fee entry is at least 0.0001 BTC to cover miner costs, ';
    }
    if( ((coin == 'MSC') || (coin == 'TMSC')) ) {
      if (sendAmount < nonZeroValue)
        error += 'make sure your send amount is non-zero, ';
      if (minerFees < minerMinimum)
        error += 'make sure your fee entry is at least 0.0001 BTC, ';
    }

    return error;
  };

  transactionGenerationController.modalTemplateUrl = '/partials/wallet_send_modal.html';

  transactionGenerationController.setModalScope = function($modalScope){
    $modalScope.transactionSuccess = false, $modalScope.transactionError = false, $modalScope.waiting = false, $modalScope.privKeyPass = {};
    $modalScope.convertSatoshiToDisplayedValue=  $scope.convertSatoshiToDisplayedValue,
    $modalScope.getDisplayedAbbreviation=  $scope.getDisplayedAbbreviation,
    $modalScope.sendAmount=  $scope.selectedCoin.divisible ? +$scope.convertDisplayedValue($scope.sendAmount) : +$scope.sendAmount,
    $modalScope.minerFees= +$scope.convertDisplayedValue($scope.minerFees),
    $modalScope.sendTo= $scope.sendTo;
    $modalScope.totalCost= +$scope.convertDisplayedValue($scope.totalCost);
  };

  transactionGenerationController.generateData = function(){
    return {
      from:$scope.selectedAddress,
      transactionType:0,
      transactionData:{
        transaction_version:0,
        transaction_from: $scope.selectedAddress,
        currency_identifier:$scope.selectedCoin.id,
        amount_to_transfer : $scope.selectedCoin.divisible ? +$scope.convertDisplayedValue($scope.sendAmount) : +$scope.sendAmount,
        transaction_to: $scope.sendTo,
        fee: $scope.convertDisplayedValue($scope.minerFees),
        marker: $scope.marked
      }
    };
  };
};

<|endoftext|>


<commit_before>angular.module('pvta.controllers').controller('MapController', function($scope, $state, $resource, $stateParams, $cordovaGeolocation, Route, Vehicle, LatLong, KML) {
  var options = {timeout: 10000, enableHighAccuracy: true};

  //set original bounds
  var bounds = new google.maps.LatLngBounds();
  //set some configurable options before initializing map
  var mapOptions = {
    // bounds is originally the entire planet, so getCenter()
    // will return the intersection of the Prime Meridian
    // and Equator by default
    center: bounds.getCenter(),
    zoom: 15,
    mapTypeId: google.maps.MapTypeId.ROADMAP
  };
  //initialize map
  $scope.map = new google.maps.Map(document.getElementById("map"), mapOptions);

  // Assuming the user has requested something
  // be placed on the map, grab the lat/long of that thing
  var desiredMarkerLocation = LatLong.getAll();
  if(desiredMarkerLocation){
    _.each(desiredMarkerLocation, function(location){

      // If our assumption was correct, query the Maps API
      // to get a valid, placeable location of our lat/long
      var loc = new google.maps.LatLng(location.lat, location.long);
      // Nested call: first, place the desired marker, then
      // add a listener for when it's tapped
      addMapListener(placeDesiredMarker(loc), "here's what you're looking for!");
      bounds.extend(loc);
    });

  }

  // Almost the same as immediately above.
  // Query Cordova for current location first.
  var currentLocation = $cordovaGeolocation.getCurrentPosition(options).then(function(position){
    var myLocation = new google.maps.LatLng(position.coords.latitude, position.coords.longitude);
    bounds.extend(myLocation);
    addMapListener(placeDesiredMarker(myLocation), 'You are here!');
  }, function(error){});

  // Takes a google.maps.LatLng object and places a marker
  // on the map in the requested spot.
  // Returns a reference to said marker.
  function placeDesiredMarker(location){
    var neededMarker = new google.maps.Marker({
        map: $scope.map,
        icon: 'http://www.google.com/mapfiles/kml/paddle/go.png',
        animation: google.maps.Animation.DROP,
        position: location
      });
    return neededMarker;
  };

  // When the map is ready, resize it to
  // the bounds that we have been setting each
  // time we add something to it.
  google.maps.event.addListenerOnce($scope.map, 'idle', function(){
      $scope.map.fitBounds(bounds);
  });

  // Takes an already-created Maps marker and
  // a string.
  // Creates an on-tap/click listener
  // that will display the onClickString
  // in a little popup when the marker is
  // tapped on.
  // Returns nothing.
  function addMapListener(marker, onClickString){
    google.maps.event.addListener(marker, 'click', function () {
            var infoWindow = new google.maps.InfoWindow({
              content: onClickString
            });
            infoWindow.open($scope.map, marker);
    });
  }

  // Query the KML service to see
  // if previous controller wanted
  // us to display a route.
  var shortName = KML.pop();
  if(shortName) {
    // If something exists, we should
    // add the route's corresponding KML
    // to the Map.
    addKML(shortName);
  }

  // Takes an Avail ShortName.
  // Uses a Maps API call to add a trace of
  // any route (by downloading its KML file from PVTA/Avail)
  // to the Map.
  // Returns nothing.
  function addKML(shortName){
    var toAdd = 'http://bustracker.pvta.com/infopoint/Resources/Traces/route' + shortName + '.kml';
    var georssLayer = new google.maps.KmlLayer({
    url: toAdd
    });
    georssLayer.setMap($scope.map);
  };
});
<commit_msg>changed icon for 'you' to be red and buses stayed green to distinguish the two<commit_after>angular.module('pvta.controllers').controller('MapController', function($scope, $state, $resource, $stateParams, $cordovaGeolocation, Route, Vehicle, LatLong, KML) {
  var options = {timeout: 10000, enableHighAccuracy: true};

  //set original bounds
  var bounds = new google.maps.LatLngBounds();
  //set some configurable options before initializing map
  var mapOptions = {
    // bounds is originally the entire planet, so getCenter()
    // will return the intersection of the Prime Meridian
    // and Equator by default
    center: bounds.getCenter(),
    zoom: 15,
    mapTypeId: google.maps.MapTypeId.ROADMAP
  };
  //initialize map
  $scope.map = new google.maps.Map(document.getElementById("map"), mapOptions);

  // Assuming the user has requested something
  // be placed on the map, grab the lat/long of that thing
  var desiredMarkerLocation = LatLong.getAll();
  if(desiredMarkerLocation){
    _.each(desiredMarkerLocation, function(location){

      // If our assumption was correct, query the Maps API
      // to get a valid, placeable location of our lat/long
      var loc = new google.maps.LatLng(location.lat, location.long);
      // Nested call: first, place the desired marker, then
      // add a listener for when it's tapped
      addMapListener(placeDesiredMarker(loc, 'http://www.google.com/mapfiles/kml/paddle/go.png'), "here's what you're looking for!");
      bounds.extend(loc);
    });

  }

  // Almost the same as immediately above.
  // Query Cordova for current location first.
  var currentLocation = $cordovaGeolocation.getCurrentPosition(options).then(function(position){
    var myLocation = new google.maps.LatLng(position.coords.latitude, position.coords.longitude);
    bounds.extend(myLocation);
    addMapListener(placeDesiredMarker(myLocation, 'http://www.google.com/mapfiles/kml/paddle/red-circle.png'), 'You are here!');
  }, function(error){});

  // Takes a google.maps.LatLng object and places a marker
  // on the map in the requested spot.
  // Returns a reference to said marker.
  function placeDesiredMarker(location, icon){
    var neededMarker = new google.maps.Marker({
        map: $scope.map,
        icon: icon,
        animation: google.maps.Animation.DROP,
        position: location
      });
    return neededMarker;
  };

  // When the map is ready, resize it to
  // the bounds that we have been setting each
  // time we add something to it.
  google.maps.event.addListenerOnce($scope.map, 'idle', function(){
      $scope.map.fitBounds(bounds);
  });

  // Takes an already-created Maps marker and
  // a string.
  // Creates an on-tap/click listener
  // that will display the onClickString
  // in a little popup when the marker is
  // tapped on.
  // Returns nothing.
  function addMapListener(marker, onClickString){
    google.maps.event.addListener(marker, 'click', function () {
            var infoWindow = new google.maps.InfoWindow({
              content: onClickString
            });
            infoWindow.open($scope.map, marker);
    });
  }

  // Query the KML service to see
  // if previous controller wanted
  // us to display a route.
  var shortName = KML.pop();
  if(shortName) {
    // If something exists, we should
    // add the route's corresponding KML
    // to the Map.
    addKML(shortName);
  }

  // Takes an Avail ShortName.
  // Uses a Maps API call to add a trace of
  // any route (by downloading its KML file from PVTA/Avail)
  // to the Map.
  // Returns nothing.
  function addKML(shortName){
    var toAdd = 'http://bustracker.pvta.com/infopoint/Resources/Traces/route' + shortName + '.kml';
    var georssLayer = new google.maps.KmlLayer({
    url: toAdd
    });
    georssLayer.setMap($scope.map);
  };
});
<|endoftext|>


<commit_before>var amok = require('../');
var test = require('tape');
var fs = require('fs');

test('compile with browserify', function(t) {
  var args = [
    'test/fixture/bundle.js'
  ];

  var exe = amok.compile('browserify', args);
  exe.stderr.on('data', function(data) {
    data = data.toString();
    t.fail(data);
  });

  exe.on('ready', function(scripts) {
    var filenames = Object.keys(scripts);
    t.equal(filenames.length, 1);

    var stats = fs.statSync(filenames[0]);
    t.ok(stats.size > 1);

    var pathnames = filenames.map(function(filename) {
      return scripts[filename];
    });

    t.equal(pathnames.length, 1);
    t.equal(pathnames[0], 'test/fixture/bundle.js');
  });

  exe.on('close', function(code, signal) {
    t.end();
  });
});
<commit_msg>Kill process to end browserify compile test<commit_after>var amok = require('../');
var test = require('tape');
var fs = require('fs');

test('compile with browserify', function(t) {
  var args = [
    'test/fixture/bundle.js'
  ];

  var exe = amok.compile('browserify', args);
  exe.stderr.on('data', function(data) {
    data = data.toString();
    t.fail(data);
  });

  exe.on('ready', function(scripts) {
    var filenames = Object.keys(scripts);
    t.equal(filenames.length, 1);

    var stats = fs.statSync(filenames[0]);
    t.ok(stats.size > 1);

    var pathnames = filenames.map(function(filename) {
      return scripts[filename];
    });

    t.equal(pathnames.length, 1);
    t.equal(pathnames[0], 'test/fixture/bundle.js');

    exe.kill();
  });

  exe.on('close', function(code, signal) {
    t.end();
  });
});
<|endoftext|>