text: /*!
   * Copyright 2014 Apereo Foundation (AF) Licensed under the
   * Educational Community 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://opensource.org/licenses/ECL-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.
   */
  
  define(['jquery', 'oae.core', 'jquery.jeditable'], function($, oae) {
  
      return function(uid) {
  
          //////////////////////
          // WIDGET VARIABLES //
          //////////////////////
  
          // The widget container
          $rootel = $('#' + uid);
  
          // Regular expression that matches valid links
          var linkRegExp = new RegExp(/^(https?|s?ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i);
  
          // Keeps track of the selected visibility for the links to add. By default, the visibility will be the configured default
          var visibility = oae.api.config.getValue('oae-content', 'visibility', 'links');
  
          // Keeps track of the links to add
          var addedLinks = [];
  
          // Keeps track of the current context (group or user)
          var contextData = null;
  
          // Generate a widget ID for the new instance of the `setpermissions` widget. This widget ID
          // will be used in the event communication between this widget and the `setpermissions` widget.
          var setPermissionsId = oae.api.util.generateId();
  
          ///////////////
          // UTILITIES //
          ///////////////
  
          /**
           * Resets the state of the widget when the modal dialog has been closed
           */
          var reset = function() {
              // Unbind the setpermissions handler
              $(document).off('oae.setpermissions.changed.' + setPermissionsId);
  
              // Resets the link visibility
              visibility = oae.api.config.getValue('oae-content', 'visibility', 'links');
  
              // Clear the previous permissions
              $('#createlink-permissions-container', $rootel).html('');
  
              // Reset the selected links list
              addedLinks = [];
  
              // Hide all steps
              $('#createlink-modal .modal-body > div', $rootel).hide();
  
              // Show the first step
              $('#createlink-link-dump-container', $rootel).show();
              $('#createlink-modal .modal-content > .modal-footer', $rootel).show();
  
              // Reset controls
              $('#createlink-next', $rootel).prop('disabled', true);
              $('#createlink-next', $rootel).show();
              $('#createlink-link-dump', $rootel).val('');
              $('#createlink-create', $rootel).hide();
              $('#createlink-create', $rootel).prop('disabled', false);
              $('#createlink-change-permissions', $rootel).prop('disabled', false);
          };
  
          /**
           * Verifies whether or not a URL is valid.
           *
           * @param  {String}           link   String to be tested as url
           * @return {Boolean|String}          Returns false if the URL is not valid. Returns the URL if it is valid.
           */
          var isValidLink = function(link) {
              // If the link doesn't start with a protocol (e.g. http://, https://, etc.), we prepend http:// as a default.
              if (!link.match(/https?:\/\/|s?ftp:\/\//)) {
                  link = 'http://' + link;
              }
              // Validate the link and return a valid link object when it passes.
              if (link && linkRegExp.test(link)) {
                  return link;
              }
              // The link is not valid, return false
              return false;
          };
  
          /**
           * Get the list of valid links that have been added/pasted by the user.
           *
           * @return {String[]}         Array containing all of the valid entered URLs
           */
          var getValidLinks = function() {
              // Split out the textarea into an array of links
              var links = $('#createlink-link-dump', $rootel).val().split('\n');
              // Remove duplicate links
              links = _.unique(links);
              // Remove invalid links
              var filteredLinks = [];
              $.each(links, function(i, link) {
                  var filteredLink = isValidLink(link);
                  if (filteredLink) {
                      filteredLinks.push(filteredLink);
                  }
              });
  
              return filteredLinks;
          };
  
          /**
           * Add a link to the list of links.
           *
           * @param  {String}   url   The link to be created
           */
          var addToSelected = function(url) {
              // Create the link object
              var linkObj = {
                  'link': url,
                  'displayName': url,
                  'description': '',
                  'resourceType': 'content',
                  'resourceSubType': 'link'
              };
  
              // Add the link to the queue
              addedLinks.push(linkObj);
          };
  
          /**
           * Saves the edited link name to the corresponding item in the array of links to add
           *
           * @param  {String}   value   The new value for the item
           * @return {String}           The value to show in the editable field after editing completed
           */
          var editableSubmitted = function(value) {
              value = $.trim(value);
              var prevValue = this.revert;
              var $listItem = $(this).parents('li');
              // If no name has been entered, we fall back to the previous value
              if (!value) {
                  return prevValue;
              } else {
                  var linkIndex = $('#createlink-selected-container li', $rootel).index($listItem);
                  addedLinks[linkIndex].displayName = value;
                  return value;
              }
          };
  
          /**
           * Shows a success or failure notification when the links have been added.
           *
           * @param  {Number}   errCount   The number of errors that occurred during the link creation
           */
          var showCompleteNotification = function(errCount) {
              // Render and show the notification
              var notificationTitle = oae.api.util.template().render($('#createlink-notification-title-template', $rootel), {
                  'context': contextData,
                  'errCount': errCount,
                  'links': addedLinks
              });
  
              var notificationBody = oae.api.util.template().render($('#createlink-notification-body-template', $rootel), {
                  'context': contextData,
                  'errCount': errCount,
                  'links': addedLinks
              });
  
              oae.api.util.notification(notificationTitle, notificationBody, errCount ? 'error' : 'success');
  
              // Hide the modal when there are no link creation errors
              if (!errCount) {
                  $('#createlink-modal', $rootel).modal('hide');
              }
          };
  
  
          /////////////////////
          // VIEW MANAGEMENT //
          /////////////////////
  
          /**
           * Shows the permissions widget to allow for updates in visiblity and members
           */
          var showPermissions = function() {
              // Hide all containers
              $('#createlink-modal .modal-body > div:visible', $rootel).hide();
              $('#createlink-modal .modal-content > .modal-footer', $rootel).hide();
              // Show the permissions container
              $('#createlink-modal .modal-body > div#createlink-permissions-container', $rootel).show();
              $('#createlink-create', $rootel).hide();
          };
  
          /**
           * Shows the overview of the selected links
           */
          var showOverview = function() {
              // Hide all containers
              $('#createlink-modal .modal-body > div:visible', $rootel).hide();
              $('#createlink-next', $rootel).hide();
              // Show the overview container
              $('#createlink-modal .modal-content > .modal-footer', $rootel).show();
              $('#createlink-modal .modal-body > div#createlink-overview-container', $rootel).show();
              $('#createlink-create', $rootel).show();
          };
  
          /**
           * Renders a list of the links to add
           */
          var renderAdded = function() {
              oae.api.util.template().render($('#createlink-selected-template', $rootel), {'links': addedLinks}, $('#createlink-selected-container', $rootel));
  
              // Initiate the widget that will deal with permission management
              setUpSetPermissions();
  
              // Give focus to the first item in the list
              $('#createlink-selected-container li:first-child', $rootel).focus();
  
              // Hide the spinning icon with jQuery (https://github.com/FortAwesome/Font-Awesome/issues/729)
              $('#createlink-selected-container li .icon-spinner', $rootel).hide();
  
              // Apply jEditable for inline editing of link names
              $('.jeditable-field', $rootel).editable(editableSubmitted, {
                  'onblur': 'submit',
                  'select' : true
              });
  
              // Apply jQuery Tooltip to the link title field to show that the fields are editable
              $('[rel="tooltip"]', $rootel).tooltip();
          };
  
  
          ////////////////////
          // INITIALIZATION //
          ////////////////////
  
          /**
           * Reset the widget when the modal dialog is closed
           */
          var setUpReset = function() {
              $('#createlink-modal', $rootel).on('hidden.bs.modal', function(ev) {
                  // Bootstrap will send out a `hidden` event when certain components are destroyed.
                  // We can only reset the widget when the modal is closed though.
                  // e.g. `$('[rel="tooltip"]', $rootel).tooltip('destroy');`
                  if ($(ev.target).hasClass('modal')) {
                      reset();
                  }
              });
          };
  
          /**
           * Removes a selected link from the list and resets the widget when no links remain
           */
          var setUpDelete = function() {
              $rootel.on('click', '.createlink-trash', function(ev) {
                  // Get the index of the list item
                  var $listItem = $(this).parents('li');
                  var linkIndex = $('#createlink-selected-container li', $rootel).index($listItem);
                  // This index corresponds to the Array from which we'll remove the added link
                  addedLinks.splice(linkIndex, 1);
                  // Also remove it from the UI
                  $listItem.fadeOut(250, function() {
                      $listItem.remove();
                      // If there are no links left, we go back to the first step
                      if (!addedLinks.length) {
                          reset();
                      }
                  });
              });
          };
  
          /**
           * Load the `setpermissions` widget into this widget. That widget will take care of permission
           * management (visibility + sharing) of the selected links
           */
          var setUpSetPermissions = function() {
              // Remove the previous `setpermissions` widget
              var $setPermissionsContainer = $('#createlink-permissions-container', $rootel);
              $setPermissionsContainer.html('');
  
              // We need to re-fetch the default visibility as the user could've selected a visibility, closed the modal and re-opened
              visibility = oae.api.config.getValue('oae-content', 'visibility', 'links');
  
              // Event that will be triggered when permission changes have been made in the `setpermissions` widget.
              $(document).on('oae.setpermissions.changed.' + setPermissionsId, function(ev, data) {
                  // Update visibility for links
                  visibility = data.visibility;
  
                  // Get an array of the viewer ids. We can filter out the current user
                  // as he will be automatically added to the links as a manager
                  var viewers = _.pluck(data.viewers, 'id');
                  viewers = _.without(viewers, oae.data.me.id);
  
                  // If the context of the page is a group context and links will be shared with the group, we
                  // set a flag that will be used to determine which notification to show upon completion
                  contextData.addedToGroupContext = _.contains(viewers, contextData.id);
  
                  // Update the members of the added links
                  $.each(addedLinks, function(index, link) {
                      link.viewers = viewers;
                  });
  
                  // Add the permissions summary
                  $('#createlink-permissions', $rootel).html(data.summary);
  
                  // Switch back to the overview
                  showOverview();
              });
  
              // Event that will be triggered when permission changes have been cancelled
              $(document).on('oae.setpermissions.cancel.' + setPermissionsId, showOverview);
  
              // Load the `setpermissions` widget into its container
              oae.api.widget.insertWidget('setpermissions', setPermissionsId, $setPermissionsContainer, false, {
                  'count': addedLinks.length,
                  'type': 'link',
                  'visibility': visibility
              });
          };
  
          /**
           * When the next button is clicked, we validate all of the entered links and progress to the next step
           * if at least 1 valid URL was entered
           */
          var setUpNextStep = function() {
              $('#createlink-next', $rootel).on('click', function() {
                  // Extract the valid links from the textarea
                  var links = getValidLinks();
  
                  // Add the valid links to the queue
                  $.each(links, function(index, link) {
                      addToSelected(link);
                  });
  
                  // If there was at least one valid link, go to the next step
                  if (addedLinks.length > 0) {
                      showOverview();
                      renderAdded();
                  }
              });
          };
  
          /**
           * Starts the link creation process. This iterates over all added links and creates them one at
           * a time. Regular updates are provided in the form of a loading spinner icon and success or fail icon
           */
          var setUpLinkCreation = function() {
              $('#createlink-create', $rootel).on('click', function() {
                  var done = 0;
                  var errCount = 0;
  
                  // Disable editing on link creation
                  $('#createlink-create', $rootel).prop('disabled', true);
                  $('.createlink-trash', $rootel).prop('disabled', true);
                  $('#createlink-change-permissions', $rootel).prop('disabled', true);
                  $('.jeditable-field', $rootel).editable('destroy');
                  $('[rel="tooltip"]', $rootel).tooltip('destroy');
  
                  /**
                   * Create the actual link
                   *
                   * @param  {Number}    index    The index of the link that's currently being created
                   */
                  var createLink = function(index) {
                      var link = addedLinks[index];
                      if (link) {
                          var $listItem = $($('#createlink-selected-container li', $rootel)[index]);
                          var $spinner = $listItem.find('.icon-spinner');
                          var $ok = $listItem.find('.icon-ok');
                          var $warning = $listItem.find('.icon-warning-sign');
  
                          // Add focus to the list element so the browser scrolls
                          $listItem.focus();
                          $spinner.show();
                          oae.api.content.createLink(link.displayName, link.description, visibility, link.link, [], link.viewers, function(error, data) {
                              $spinner.hide();
                              if (!error) {
                                  $ok.show();
                                  // Update the file object with the profile path of the content
                                  link.profilePath = data.profilePath;
                              } else {
                                  $warning.show();
                                  // Update the error count
                                  errCount++;
                              }
  
                              done++;
                              if (done !== addedLinks.length) {
                                  createLink(done);
                              } else {
                                  $(window).trigger('done.addcontent.oae');
                                  showCompleteNotification(errCount);
                              }
                          });
                      }
                  };
  
                  createLink(0);
              });
          };
  
          /**
           * Handles changes in the link dump textarea. If no valid links are available into the area the `next` button will be disabled.
           */
          var setUpLinkDump = function() {
              $('#createlink-link-dump', $rootel).on('keyup paste', function(ev) {
                  // The paste event fires before the textarea values changes so we add a small timeout
                  setTimeout(function () {
                      // Extract the valid links from the textarea
                      var links = getValidLinks();
  
                      if (links.length) {
                          $('#createlink-next', $rootel).prop('disabled', false);
                      } else {
                          $('#createlink-next', $rootel).prop('disabled', true);
                      }
                  }, 1);
              });
          };
  
          /**
           * Initializes the create link modal dialog
           */
          var setUpLinkModal = function() {
              $(document).on('click', '.oae-trigger-createlink', function() {
                  $('#createlink-modal', $rootel).modal({
                      'backdrop': 'static'
                  });
              });
  
              $('#createlink-modal', $rootel).on('shown.bs.modal', function() {
                  // IE10 has a problem where it treats the placeholder text as the textarea's
                  // value. Therefore, we need to explicitly clear the value of the textarea to
                  // make the placeholder behave like a placeholder.
                  // @see https://github.com/oaeproject/3akai-ux/pull/2906
                  $('#createlink-link-dump', $rootel).val('');
                  // Set focus to the link dump textarea
                  $('#createlink-link-dump', $rootel).focus();
              });
  
              // Binds the 'change' button that shows the setpermissions widget
              $rootel.on('click', '.setpermissions-change-permissions', showPermissions);
  
              // Receive the context information and cache it
              $(document).on('oae.context.send.createlink', function(ev, ctx) {
                  contextData = ctx;
              });
  
              // Request the context information
              $(document).trigger('oae.context.get', 'createlink');
          };
  
          setUpReset();
          setUpNextStep();
          setUpDelete();
          setUpLinkDump();
          setUpLinkModal();
          setUpLinkCreation();
  
      };
  });
  