<commit_before>(function () {
"use strict";

var require = window.require = makeRequire("/");
require.resolve = resolve;
function makeRequire(root) {
function require(path) {
  return realRequire(root, path);
}
function requireAsync(path, callback) {
  realRequireAsync(root, path, function (module) {
    callback(null, module);
  }, callback);
}
require.async = requireAsync;
return require;
}

////////////////////////////////////////////////////////////////////////////////
// Get source files via XHR.
// Protect calls to get to cache and not allow concurrent requests.
// This I/O can be very expensive.
var files = {};
var missing = {};
var pendingGet = {};
function get(path, callback, errback) {
  if (path in files) return callback(path, files[path]);
  if (path in missing) return errback(missing[path]);
  if (path in pendingGet) return pendingGet[path].push([callback, errback]);
  pendingGet[path] = [[callback, errback]];
  realGet(path, function (contents) {
    files[path] = contents;
    flush();
  }, function (err) {
    missing[path] = err;
    flush();
  });
  function flush() {
    var pending = pendingGet[path];
    delete pendingGet[path];
    pending.forEach(function (pair) {
      get(path, pair[0], pair[1]);
    });
  }
}
function realGet(path, callback, errback) {
  var request = new XMLHttpRequest();
  request.onload = function () {
    callback(this.responseText);
  };
  request.onerror = function () {
    errback(this);
  };
  request.open("GET", path + "?" + (Math.random() * 0x100000000).toString(36), true);
  request.setRequestHeader("Pragma", "no-cache");
  request.send();
}
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Use a temporary filesystem to write the wrapped js files.
// The writeFile function outputs the url the file that a script tag can use.
var fs;
var requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem;
requestFileSystem(window.TEMPORARY, null, function (fileSystem) {
  fs = fileSystem;
  window.modulesReady();
}, function (fileError) {
  throw new Error("Unable to create temporary fs for module loader: " + fileError);
});
function writeFile(path, contents, callback, errback) {
  fs.root.getFile(path, {create: true}, function (fileEntry) {
    fileEntry.createWriter(function (fileWriter) {
      var truncated = false;
      fileWriter.onwriteend = function () {
        if (!truncated) {
          truncated = true;
          this.truncate(this.position);
          return;
        }
        callback(fileEntry.toURL());
      };
      fileWriter.onerror = errback;
      fileWriter.write(new Blob([contents], {type: 'text/plain'}));
    }, errback);
  }, errback);
}
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Resolve relative paths to absolute paths.
// Also implements module lookup logic and automatic file extensions.
// Root is always an absolute path starting and ending with a `/`
// path is the actual string used in require.
// If path starts with a `/`, then it's an absolute require.
// If paths starts with a `.`, then it's a relative require.
// Otherwise it's a module path and we must search for it.
// We also want to support optional extensions, loading json files, and
// parsing package.json looking for main.
// callback(realPath, contents) or errback(err) are the output.
var mappings = {};
require.mappings = mappings;
require.resolve = resolve;
function resolve(root, path, callback, errback) {
  function success(fullPath, contents) {
    mappings[root + ":" + path] = fullPath;
    callback(fullPath, contents);
  }
  if (path[0] === "/") return find(path, success, errback);
  if (path[0] === ".") return find(realPath(root + path), success, errback);
  var base = root;
  cycle();
  function cycle() {
    base = base.match(/^(.*\/)[^\/]*$/)[1];
    find(base + "modules/" + path, success, function () {
      if (base.length > 1) {
        base = base.substr(0, base.length - 1);
        cycle();
      }
      else {
        errback(new Error("Can't find module: " + path + " in " + root));
      }
    });
  }
}
function realPath(path) {
  var match;
  // Remove /./ entries from path
  while (match = path.match(/(\/)\.\//)) {
    path = path.replace(match[0], match[1]);
  }
  // Convert /foo/../ entries from path
  while (match = path.match(/(\/[^\/]*\/)\.\.\//)) {
    path = path.replace(match[0], match[1]);
  }
  return path;
}
function find(path, callback, errback) {
  // .js extensions are passed through as-is
  if (/\.js$/.test(path)) return get(path, callback, errback);
  // Then try looking for package.json
  // Then try appending the .js extension
  get(path + "/package.json", function (jsonPath, json) {
    // Parse the JSON file
    var doc;
    try { doc = JSON.parse(json); }
    catch (err) { return errback(err); }
    // Abort if main is missing
    if (!doc.main) {
      return errback(new Error("Missing main field in " + jsonPath));
    }
    find(realPath(path + "/" + doc.main), callback, errback);
  }, function () {
    get(path + ".js", callback, function () {
      errback(new Error("Unable to find module: " + path));
    });
  });
}
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Load loads a module and all it's dependencies, wraps them in AMD and inserts
// them as script tags.
function load(root, path, callback, errback) {
  resolve(root, path, function (realPath, contents) {
    process(realPath, contents, callback, errback);
  }, errback);
}
var defs = {};
require.defs = defs;
var defCallbacks = {};
window.define = define;
function define(path, deps, fn) {
  var def = { path: path, deps: deps, fn: fn };
  defs[path] = def;
  var callback = defCallbacks[path];
  delete defCallbacks[path];
  callback(def);
}
var processPending = {};
function process(path, contents, callback, errback) {
  if (path in defs) return callback(defs[path]);
  if (path in processPending) {
    return processPending[path].push([callback, errback]);
  }
  processPending[path] = [[callback, errback]];
  realProcess(path, contents, function (def) {
    defs[path] = def;
    flush(null, def);
  }, flush);
  function flush(err, def) {
    var pending = processPending[path];
    delete processPending[path];
    pending.forEach(function (pair) {
      if (err) pair[1](err);
      else pair[0](def);
    });
  }
}
function realProcess(path, contents, callback, errback) {
  var failed;
  function fail(err) {
    if (failed) return;
    failed = true;
    errback(err);
  }
  // Scan for dependencies
  var root = path.match(/^(.*\/)[^\/]*$/)[1];
  var matches = mine(contents);
  var deps = [];
  if (!matches) return save();
  // If there are dependencies, load them first.
  var left = matches.length;
  for (var i = 0, l = left; i < l; i++) {
    var match = matches[i];
    resolve(root, match, onResolve, fail);
  }
  function onResolve(realPath, contents) {
    deps.push(realPath);
    process(realPath, contents, check, fail);
  }
  function check(dep) {
    deps.push(dep);
    if (--left) return;
    deps.sort();
    save();
  }

  function save() {
    // Wrap and save the file
    var wrappedjs = 'window.define(' + JSON.stringify(path) + ', ' + JSON.stringify(deps) +
      ', function (module, exports, require, __dirname, __filename) {\n' + contents + '\n});';
    writeFile(path.substr(1).replace(/\//g, "_"), wrappedjs, function (url) {
      defCallbacks[path] = callback;
      var script = document.createElement('script');
      script.type = 'text/javascript';
      script.src = url;
      document.head.appendChild(script);
    }, errback);
  }
}
// Mine a string for require calls and export the module names
// Extract all require calls using a proper state-machine parser.
function mine(js) {
  var names = [];
  var state = "START";
  var ident;
  var quote;
  var name;
  for (var i = 0, l = js.length; i < l; i++) {
    var char = js[i];
    switch (state) {
      case "START":
        if (char === "/") state = "SLASH";
        else if (char === "'" || char === '"') {
          quote = char;
          state = "STRING";
        }
        else if (char === "r") {
          ident = char;
          state = "IDENT";
        }
        break;
      case "IDENT":
        if (char === "require"[ident.length]) {
          ident += char;
        }
        else if (char === "(" && ident === "require") {
          ident = undefined;
          state = "CALL";
        }
        else {
          state = "START";
        }
        break;
      case "CALL":
        if (char === "'" || char === '"') {
          quote = char;
          name = "";
          state = "NAME";
        }
        else {
          state = "START";
        }
        break;
      case "NAME":
        if (char === quote) {
          names.push(name);
          name = undefined;
          state = "START";
        }
        else {
          name += char;
        }
        break;
      case "STRING":
        if (char === "\\") {
          state = "ESCAPE";
        }
        else if (char === quote) {
          state = "START";
        }
        break;
      case "ESCAPE":
        state = "STRING";
        break;
      case "SLASH":
        if (char === "/") state = "LINE_COMMENT";
        else if (char === "*") state = "MULTILINE_COMMENT";
        else state = "START";
        break;
      case "LINE_COMMENT":
        if (char === "\r" || char === "\n") state = "START";
        break;
      case "MULTILINE_COMMENT":
        if (char === "*") state = "MULTILINE_ENDING";
        break;
      case "MULTILINE_ENDING":
        if (char === "/") state = "START";
        else if (char !== "*") state = "MULTILINE_COMMENT";
        break;
    }
  }
  return names;
}
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
var modules = {};
require.modules = modules;
function realRequire(root, path) {
  var realPath = mappings[root + ":" + path];
  if (!realPath) throw new Error("Can't require sync yet: " + path + " in " + root);
  var def = defs[realPath];
  if (!def) throw new Error("Missing definition for: " + realPath);
  return start(def);
}
function realRequireAsync(root, path, callback, errback) {
  load(root, path, function (def) {
    var module;
    try {
      module = start(def);
    }
    catch (err) {
      return errback(err);
    }
    callback(module);
  }, errback);
}

function start(def) {
  var pathname = def.path;
  var dirname = pathname.match(/^(.*\/)[^\/]*$/)[1];
  var require = makeRequire(dirname);
  var exports = {};
  var module = {exports: exports};
  delete defs[pathname];
  modules[pathname] = exports;
  def.fn(module, exports, require, dirname, pathname);
  modules[pathname] = module.exports;
  return module.exports;
}

}());
<commit_msg>with mine, matches is always an array<commit_after>(function () {
"use strict";

var require = window.require = makeRequire("/");
require.resolve = resolve;
function makeRequire(root) {
function require(path) {
  return realRequire(root, path);
}
function requireAsync(path, callback) {
  realRequireAsync(root, path, function (module) {
    callback(null, module);
  }, callback);
}
require.async = requireAsync;
return require;
}

////////////////////////////////////////////////////////////////////////////////
// Get source files via XHR.
// Protect calls to get to cache and not allow concurrent requests.
// This I/O can be very expensive.
var files = {};
var missing = {};
var pendingGet = {};
function get(path, callback, errback) {
  if (path in files) return callback(path, files[path]);
  if (path in missing) return errback(missing[path]);
  if (path in pendingGet) return pendingGet[path].push([callback, errback]);
  pendingGet[path] = [[callback, errback]];
  realGet(path, function (contents) {
    files[path] = contents;
    flush();
  }, function (err) {
    missing[path] = err;
    flush();
  });
  function flush() {
    var pending = pendingGet[path];
    delete pendingGet[path];
    pending.forEach(function (pair) {
      get(path, pair[0], pair[1]);
    });
  }
}
function realGet(path, callback, errback) {
  var request = new XMLHttpRequest();
  request.onload = function () {
    callback(this.responseText);
  };
  request.onerror = function () {
    errback(this);
  };
  request.open("GET", path + "?" + (Math.random() * 0x100000000).toString(36), true);
  request.setRequestHeader("Pragma", "no-cache");
  request.send();
}
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Use a temporary filesystem to write the wrapped js files.
// The writeFile function outputs the url the file that a script tag can use.
var fs;
var requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem;
requestFileSystem(window.TEMPORARY, null, function (fileSystem) {
  fs = fileSystem;
  window.modulesReady();
}, function (fileError) {
  throw new Error("Unable to create temporary fs for module loader: " + fileError);
});
function writeFile(path, contents, callback, errback) {
  fs.root.getFile(path, {create: true}, function (fileEntry) {
    fileEntry.createWriter(function (fileWriter) {
      var truncated = false;
      fileWriter.onwriteend = function () {
        if (!truncated) {
          truncated = true;
          this.truncate(this.position);
          return;
        }
        callback(fileEntry.toURL());
      };
      fileWriter.onerror = errback;
      fileWriter.write(new Blob([contents], {type: 'text/plain'}));
    }, errback);
  }, errback);
}
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Resolve relative paths to absolute paths.
// Also implements module lookup logic and automatic file extensions.
// Root is always an absolute path starting and ending with a `/`
// path is the actual string used in require.
// If path starts with a `/`, then it's an absolute require.
// If paths starts with a `.`, then it's a relative require.
// Otherwise it's a module path and we must search for it.
// We also want to support optional extensions, loading json files, and
// parsing package.json looking for main.
// callback(realPath, contents) or errback(err) are the output.
var mappings = {};
require.mappings = mappings;
require.resolve = resolve;
function resolve(root, path, callback, errback) {
  function success(fullPath, contents) {
    mappings[root + ":" + path] = fullPath;
    callback(fullPath, contents);
  }
  if (path[0] === "/") return find(path, success, errback);
  if (path[0] === ".") return find(realPath(root + path), success, errback);
  var base = root;
  cycle();
  function cycle() {
    base = base.match(/^(.*\/)[^\/]*$/)[1];
    find(base + "modules/" + path, success, function () {
      if (base.length > 1) {
        base = base.substr(0, base.length - 1);
        cycle();
      }
      else {
        errback(new Error("Can't find module: " + path + " in " + root));
      }
    });
  }
}
function realPath(path) {
  var match;
  // Remove /./ entries from path
  while (match = path.match(/(\/)\.\//)) {
    path = path.replace(match[0], match[1]);
  }
  // Convert /foo/../ entries from path
  while (match = path.match(/(\/[^\/]*\/)\.\.\//)) {
    path = path.replace(match[0], match[1]);
  }
  return path;
}
function find(path, callback, errback) {
  // .js extensions are passed through as-is
  if (/\.js$/.test(path)) return get(path, callback, errback);
  // Then try looking for package.json
  // Then try appending the .js extension
  get(path + "/package.json", function (jsonPath, json) {
    // Parse the JSON file
    var doc;
    try { doc = JSON.parse(json); }
    catch (err) { return errback(err); }
    // Abort if main is missing
    if (!doc.main) {
      return errback(new Error("Missing main field in " + jsonPath));
    }
    find(realPath(path + "/" + doc.main), callback, errback);
  }, function () {
    get(path + ".js", callback, function () {
      errback(new Error("Unable to find module: " + path));
    });
  });
}
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Load loads a module and all it's dependencies, wraps them in AMD and inserts
// them as script tags.
function load(root, path, callback, errback) {
  resolve(root, path, function (realPath, contents) {
    process(realPath, contents, callback, errback);
  }, errback);
}
var defs = {};
require.defs = defs;
var defCallbacks = {};
window.define = define;
function define(path, deps, fn) {
  var def = { path: path, deps: deps, fn: fn };
  defs[path] = def;
  var callback = defCallbacks[path];
  delete defCallbacks[path];
  callback(def);
}
var processPending = {};
function process(path, contents, callback, errback) {
  if (path in defs) return callback(defs[path]);
  if (path in processPending) {
    return processPending[path].push([callback, errback]);
  }
  processPending[path] = [[callback, errback]];
  realProcess(path, contents, function (def) {
    defs[path] = def;
    flush(null, def);
  }, flush);
  function flush(err, def) {
    var pending = processPending[path];
    delete processPending[path];
    pending.forEach(function (pair) {
      if (err) pair[1](err);
      else pair[0](def);
    });
  }
}
function realProcess(path, contents, callback, errback) {
  var failed;
  function fail(err) {
    if (failed) return;
    failed = true;
    errback(err);
  }
  // Scan for dependencies
  var root = path.match(/^(.*\/)[^\/]*$/)[1];
  var matches = mine(contents);
  var deps = [];
  if (!matches.length) return save();
  // If there are dependencies, load them first.
  var left = matches.length;
  for (var i = 0, l = left; i < l; i++) {
    var match = matches[i];
    resolve(root, match, onResolve, fail);
  }
  function onResolve(realPath, contents) {
    deps.push(realPath);
    process(realPath, contents, check, fail);
  }
  function check(dep) {
    deps.push(dep);
    if (--left) return;
    deps.sort();
    save();
  }

  function save() {
    // Wrap and save the file
    var wrappedjs = 'window.define(' + JSON.stringify(path) + ', ' + JSON.stringify(deps) +
      ', function (module, exports, require, __dirname, __filename) {\n' + contents + '\n});';
    writeFile(path.substr(1).replace(/\//g, "_"), wrappedjs, function (url) {
      defCallbacks[path] = callback;
      var script = document.createElement('script');
      script.type = 'text/javascript';
      script.src = url;
      document.head.appendChild(script);
    }, errback);
  }
}
// Mine a string for require calls and export the module names
// Extract all require calls using a proper state-machine parser.
function mine(js) {
  var names = [];
  var state = "START";
  var ident;
  var quote;
  var name;
  for (var i = 0, l = js.length; i < l; i++) {
    var char = js[i];
    switch (state) {
      case "START":
        if (char === "/") state = "SLASH";
        else if (char === "'" || char === '"') {
          quote = char;
          state = "STRING";
        }
        else if (char === "r") {
          ident = char;
          state = "IDENT";
        }
        break;
      case "IDENT":
        if (char === "require"[ident.length]) {
          ident += char;
        }
        else if (char === "(" && ident === "require") {
          ident = undefined;
          state = "CALL";
        }
        else {
          state = "START";
        }
        break;
      case "CALL":
        if (char === "'" || char === '"') {
          quote = char;
          name = "";
          state = "NAME";
        }
        else {
          state = "START";
        }
        break;
      case "NAME":
        if (char === quote) {
          names.push(name);
          name = undefined;
          state = "START";
        }
        else {
          name += char;
        }
        break;
      case "STRING":
        if (char === "\\") {
          state = "ESCAPE";
        }
        else if (char === quote) {
          state = "START";
        }
        break;
      case "ESCAPE":
        state = "STRING";
        break;
      case "SLASH":
        if (char === "/") state = "LINE_COMMENT";
        else if (char === "*") state = "MULTILINE_COMMENT";
        else state = "START";
        break;
      case "LINE_COMMENT":
        if (char === "\r" || char === "\n") state = "START";
        break;
      case "MULTILINE_COMMENT":
        if (char === "*") state = "MULTILINE_ENDING";
        break;
      case "MULTILINE_ENDING":
        if (char === "/") state = "START";
        else if (char !== "*") state = "MULTILINE_COMMENT";
        break;
    }
  }
  return names;
}
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
var modules = {};
require.modules = modules;
function realRequire(root, path) {
  var realPath = mappings[root + ":" + path];
  if (!realPath) throw new Error("Can't require sync yet: " + path + " in " + root);
  var def = defs[realPath];
  if (!def) throw new Error("Missing definition for: " + realPath);
  return start(def);
}
function realRequireAsync(root, path, callback, errback) {
  load(root, path, function (def) {
    var module;
    try {
      module = start(def);
    }
    catch (err) {
      return errback(err);
    }
    callback(module);
  }, errback);
}

function start(def) {
  var pathname = def.path;
  var dirname = pathname.match(/^(.*\/)[^\/]*$/)[1];
  var require = makeRequire(dirname);
  var exports = {};
  var module = {exports: exports};
  delete defs[pathname];
  modules[pathname] = exports;
  def.fn(module, exports, require, dirname, pathname);
  modules[pathname] = module.exports;
  return module.exports;
}

}());
<|endoftext|>


<commit_before>/**
 * The following features are still outstanding: animation as a
 * function, placement as a function, inside, support for more triggers than
 * just mouse enter/leave, html tooltips, and selector delegation.
 */
angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position'])

/**
 * The $tooltip service creates tooltip- and popover-like directives as well as
 * houses global options for them.
 */
.provider('$tooltip', function() {
  // The default options tooltip and popover.
  var defaultOptions = {
    placement: 'top',
    animation: true,
    popupDelay: 0,
    useContentExp: false
  };

  // Default hide triggers for each show trigger
  var triggerMap = {
    'mouseenter': 'mouseleave',
    'click': 'click',
    'focus': 'blur',
    'none': ''
  };

  // The options specified to the provider globally.
  var globalOptions = {};

  /**
   * `options({})` allows global configuration of all tooltips in the
   * application.
   *
   *   var app = angular.module( 'App', ['ui.bootstrap.tooltip'], function( $tooltipProvider ) {
   *     // place tooltips left instead of top by default
   *     $tooltipProvider.options( { placement: 'left' } );
   *   });
   */
	this.options = function(value) {
		angular.extend(globalOptions, value);
	};

  /**
   * This allows you to extend the set of trigger mappings available. E.g.:
   *
   *   $tooltipProvider.setTriggers( 'openTrigger': 'closeTrigger' );
   */
  this.setTriggers = function setTriggers(triggers) {
    angular.extend(triggerMap, triggers);
  };

  /**
   * This is a helper function for translating camel-case to snake-case.
   */
  function snake_case(name) {
    var regexp = /[A-Z]/g;
    var separator = '-';
    return name.replace(regexp, function(letter, pos) {
      return (pos ? separator : '') + letter.toLowerCase();
    });
  }

  /**
   * Returns the actual instance of the $tooltip service.
   * TODO support multiple triggers
   */
  this.$get = ['$window', '$compile', '$timeout', '$document', '$position', '$interpolate', '$rootScope', '$parse', function($window, $compile, $timeout, $document, $position, $interpolate, $rootScope, $parse) {
    return function $tooltip(type, prefix, defaultTriggerShow, options) {
      options = angular.extend({}, defaultOptions, globalOptions, options);

      /**
       * Returns an object of show and hide triggers.
       *
       * If a trigger is supplied,
       * it is used to show the tooltip; otherwise, it will use the `trigger`
       * option passed to the `$tooltipProvider.options` method; else it will
       * default to the trigger supplied to this directive factory.
       *
       * The hide trigger is based on the show trigger. If the `trigger` option
       * was passed to the `$tooltipProvider.options` method, it will use the
       * mapped trigger from `triggerMap` or the passed trigger if the map is
       * undefined; otherwise, it uses the `triggerMap` value of the show
       * trigger; else it will just use the show trigger.
       */
      function getTriggers(trigger) {
        var show = (trigger || options.trigger || defaultTriggerShow).split(' ');
        var hide = show.map(function(trigger) {
          return triggerMap[trigger] || trigger;
        });
        return {
          show: show,
          hide: hide
        };
      }

      var directiveName = snake_case(type);

      var startSym = $interpolate.startSymbol();
      var endSym = $interpolate.endSymbol();
      var template =
        '<div '+ directiveName +'-popup '+
          'title="'+startSym+'title'+endSym+'" '+
          (options.useContentExp ?
            'content-exp="contentExp()" ' :
            'content="'+startSym+'content'+endSym+'" ') +
          'placement="'+startSym+'placement'+endSym+'" '+
          'popup-class="'+startSym+'popupClass'+endSym+'" '+
          'animation="animation" '+
          'is-open="isOpen"'+
          'origin-scope="origScope" '+
          '>'+
        '</div>';

      return {
        restrict: 'EA',
        compile: function(tElem, tAttrs) {
          var tooltipLinker = $compile( template );

          return function link(scope, element, attrs, tooltipCtrl) {
            var tooltip;
            var tooltipLinkedScope;
            var transitionTimeout;
            var popupTimeout;
            var positionTimeout;
            var appendToBody = angular.isDefined(options.appendToBody) ? options.appendToBody : false;
            var triggers = getTriggers(undefined);
            var hasEnableExp = angular.isDefined(attrs[prefix + 'Enable']);
            var ttScope = scope.$new(true);
            var repositionScheduled = false;
            var isOpenExp = angular.isDefined(attrs[prefix + 'IsOpen']) ? $parse(attrs[prefix + 'IsOpen']) : false;

            var positionTooltip = function() {
              // check if tooltip exists and is not empty
              if (!tooltip || !tooltip.html()) { return; }

              if (!positionTimeout) {
                positionTimeout = $timeout(function() {
                  // Reset the positioning and box size for correct width and height values.
                  tooltip.css({ top: 0, left: 0, width: 'auto', height: 'auto', visibility: 'none' });

                  var ttBox = $position.position(tooltip);
                  var ttCss = $position.positionElements(element, tooltip, ttScope.placement, appendToBody);
                  ttCss.top += 'px';
                  ttCss.left += 'px';

                  ttCss.width = ttBox.width + 'px';
                  ttCss.height = ttBox.height + 'px';

                  ttCss.visibility = 'visible';

                  // Now set the calculated positioning and size.
                  tooltip.css(ttCss);

                  positionTimeout = null;

                }, 0, false);
              }
            };

            // Set up the correct scope to allow transclusion later
            ttScope.origScope = scope;

            // By default, the tooltip is not open.
            // TODO add ability to start tooltip opened
            ttScope.isOpen = false;

            function toggleTooltipBind() {
              if (!ttScope.isOpen) {
                showTooltipBind();
              } else {
                hideTooltipBind();
              }
            }

            // Show the tooltip with delay if specified, otherwise show it immediately
            function showTooltipBind() {
              if (hasEnableExp && !scope.$eval(attrs[prefix + 'Enable'])) {
                return;
              }

              prepareTooltip();

              if (ttScope.popupDelay) {
                // Do nothing if the tooltip was already scheduled to pop-up.
                // This happens if show is triggered multiple times before any hide is triggered.
                if (!popupTimeout) {
                  popupTimeout = $timeout(show, ttScope.popupDelay, false);
                }
              } else {
                show();
              }
            }

            function hideTooltipBind () {
              hide();
              if (!$rootScope.$$phase) {
                $rootScope.$digest();
              }
            }

            // Show the tooltip popup element.
            function show() {
              popupTimeout = null;

              // If there is a pending remove transition, we must cancel it, lest the
              // tooltip be mysteriously removed.
              if (transitionTimeout) {
                $timeout.cancel(transitionTimeout);
                transitionTimeout = null;
              }

              // Don't show empty tooltips.
              if (!(options.useContentExp ? ttScope.contentExp() : ttScope.content)) {
                return angular.noop;
              }

              createTooltip();

              // And show the tooltip.
              ttScope.isOpen = true;
              if (isOpenExp) {
                isOpenExp.assign(ttScope.origScope, ttScope.isOpen);
              }

              if (!$rootScope.$$phase) {
                ttScope.$apply(); // digest required as $apply is not called
              }

              tooltip.css({ display: 'block', visibility: 'hidden' });

              positionTooltip();
            }

            // Hide the tooltip popup element.
            function hide() {
              if (!ttScope) {
                return;
              }

              // First things first: we don't show it anymore.
              ttScope.isOpen = false;
              if (isOpenExp) {
                isOpenExp.assign(ttScope.origScope, ttScope.isOpen);
              }

              //if tooltip is going to be shown after delay, we must cancel this
              $timeout.cancel(popupTimeout);
              popupTimeout = null;

              $timeout.cancel(positionTimeout);
              positionTimeout = null;

              // And now we remove it from the DOM. However, if we have animation, we
              // need to wait for it to expire beforehand.
              // FIXME: this is a placeholder for a port of the transitions library.
              if (ttScope.animation) {
                if (!transitionTimeout) {
                  transitionTimeout = $timeout(removeTooltip, 500);
                }
              } else {
                removeTooltip();
              }
            }

            function createTooltip() {
              // There can only be one tooltip element per directive shown at once.
              if (tooltip) {
                removeTooltip();
              }
              tooltipLinkedScope = ttScope.$new();
              tooltip = tooltipLinker(tooltipLinkedScope, function(tooltip) {
                if (appendToBody) {
                  $document.find('body').append(tooltip);
                } else {
                  element.after(tooltip);
                }
              });

              if (options.useContentExp) {
                tooltipLinkedScope.$watch('contentExp()', function(val) {
                  if (!val && ttScope.isOpen) {
                    hide();
                  }
                });

                tooltipLinkedScope.$watch(function() {
                  if (!repositionScheduled) {
                    repositionScheduled = true;
                    tooltipLinkedScope.$$postDigest(function() {
                      repositionScheduled = false;
                      if (ttScope.isOpen) {
                        positionTooltip();
                      }
                    });
                  }
                });

              }
            }

            function removeTooltip() {
              transitionTimeout = null;
              if (tooltip) {
                tooltip.remove();
                tooltip = null;
              }
              if (tooltipLinkedScope) {
                tooltipLinkedScope.$destroy();
                tooltipLinkedScope = null;
              }
            }

            function prepareTooltip() {
              prepPopupClass();
              prepPlacement();
              prepPopupDelay();
            }

            ttScope.contentExp = function() {
              return scope.$eval(attrs[type]);
            };

            /**
             * Observe the relevant attributes.
             */
            if (!options.useContentExp) {
              attrs.$observe(type, function(val) {
                ttScope.content = val;

                if (!val && ttScope.isOpen) {
                  hide();
                } else {
                  positionTooltip();
                }
              });
            }

            attrs.$observe('disabled', function(val) {
              if (popupTimeout && val) {
                $timeout.cancel(popupTimeout);
                popupTimeout = null;
              }

              if (val && ttScope.isOpen) {
                hide();
              }
            });

            attrs.$observe(prefix + 'Title', function(val) {
              ttScope.title = val;
              positionTooltip();
            });

            attrs.$observe(prefix + 'Placement', function() {
              if (ttScope.isOpen) {
                prepPlacement();
                positionTooltip();
              }
            });

            if (isOpenExp) {
              scope.$watch(isOpenExp, function(val) {
                if (val !== ttScope.isOpen) {
                  toggleTooltipBind();
                }
              });
            }

            function prepPopupClass() {
              ttScope.popupClass = attrs[prefix + 'Class'];
            }

            function prepPlacement() {
              var val = attrs[prefix + 'Placement'];
              ttScope.placement = angular.isDefined(val) ? val : options.placement;
            }

            function prepPopupDelay() {
              var val = attrs[prefix + 'PopupDelay'];
              var delay = parseInt(val, 10);
              ttScope.popupDelay = !isNaN(delay) ? delay : options.popupDelay;
            }

            var unregisterTriggers = function() {
              triggers.show.forEach(function(trigger) {
                element.unbind(trigger, showTooltipBind);
              });
              triggers.hide.forEach(function(trigger) {
                element.unbind(trigger, hideTooltipBind);
              });
            };

            function prepTriggers() {
              var val = attrs[prefix + 'Trigger'];
              unregisterTriggers();

              triggers = getTriggers(val);

              if (triggers.show !== 'none') {
                triggers.show.forEach(function(trigger, idx) {
                  // Using raw addEventListener due to jqLite/jQuery bug - #4060
                  if (trigger === triggers.hide[idx]) {
                    element[0].addEventListener(trigger, toggleTooltipBind);
                  } else if (trigger) {
                    element[0].addEventListener(trigger, showTooltipBind);
                    element[0].addEventListener(triggers.hide[idx], hideTooltipBind);
                  }
                });
              }
            }
            prepTriggers();

            var animation = scope.$eval(attrs[prefix + 'Animation']);
            ttScope.animation = angular.isDefined(animation) ? !!animation : options.animation;

            var appendToBodyVal = scope.$eval(attrs[prefix + 'AppendToBody']);
            appendToBody = angular.isDefined(appendToBodyVal) ? appendToBodyVal : appendToBody;

            // if a tooltip is attached to <body> we need to remove it on
            // location change as its parent scope will probably not be destroyed
            // by the change.
            if (appendToBody) {
              scope.$on('$locationChangeSuccess', function closeTooltipOnLocationChangeSuccess() {
                if (ttScope.isOpen) {
                  hide();
                }
              });
            }

            // Make sure tooltip is destroyed and removed.
            scope.$on('$destroy', function onDestroyTooltip() {
              $timeout.cancel(transitionTimeout);
              $timeout.cancel(popupTimeout);
              $timeout.cancel(positionTimeout);
              unregisterTriggers();
              removeTooltip();
              ttScope = null;
            });
          };
        }
      };
    };
  }];
})

// This is mostly ngInclude code but with a custom scope
.directive('tooltipTemplateTransclude', [
         '$animate', '$sce', '$compile', '$templateRequest',
function ($animate ,  $sce ,  $compile ,  $templateRequest) {
  return {
    link: function(scope, elem, attrs) {
      var origScope = scope.$eval(attrs.tooltipTemplateTranscludeScope);

      var changeCounter = 0,
        currentScope,
        previousElement,
        currentElement;

      var cleanupLastIncludeContent = function() {
        if (previousElement) {
          previousElement.remove();
          previousElement = null;
        }
        if (currentScope) {
          currentScope.$destroy();
          currentScope = null;
        }
        if (currentElement) {
          $animate.leave(currentElement).then(function() {
            previousElement = null;
          });
          previousElement = currentElement;
          currentElement = null;
        }
      };

      scope.$watch($sce.parseAsResourceUrl(attrs.tooltipTemplateTransclude), function(src) {
        var thisChangeId = ++changeCounter;

        if (src) {
          //set the 2nd param to true to ignore the template request error so that the inner
          //contents and scope can be cleaned up.
          $templateRequest(src, true).then(function(response) {
            if (thisChangeId !== changeCounter) { return; }
            var newScope = origScope.$new();
            var template = response;

            var clone = $compile(template)(newScope, function(clone) {
              cleanupLastIncludeContent();
              $animate.enter(clone, elem);
            });

            currentScope = newScope;
            currentElement = clone;

            currentScope.$emit('$includeContentLoaded', src);
          }, function() {
            if (thisChangeId === changeCounter) {
              cleanupLastIncludeContent();
              scope.$emit('$includeContentError', src);
            }
          });
          scope.$emit('$includeContentRequested', src);
        } else {
          cleanupLastIncludeContent();
        }
      });

      scope.$on('$destroy', cleanupLastIncludeContent);
    }
  };
}])

/**
 * Note that it's intentional that these classes are *not* applied through $animate.
 * They must not be animated as they're expected to be present on the tooltip on
 * initialization.
 */
.directive('tooltipClasses', function() {
  return {
    restrict: 'A',
    link: function(scope, element, attrs) {
      if (scope.placement) {
        element.addClass(scope.placement);
      }
      if (scope.popupClass) {
        element.addClass(scope.popupClass);
      }
      if (scope.animation()) {
        element.addClass(attrs.tooltipAnimationClass);
      }
    }
  };
})

.directive('tooltipPopup', function() {
  return {
    restrict: 'EA',
    replace: true,
    scope: { content: '@', placement: '@', popupClass: '@', animation: '&', isOpen: '&' },
    templateUrl: 'template/tooltip/tooltip-popup.html'
  };
})

.directive('tooltip', [ '$tooltip', function($tooltip) {
  return $tooltip('tooltip', 'tooltip', 'mouseenter');
}])

.directive('tooltipTemplatePopup', function() {
  return {
    restrict: 'EA',
    replace: true,
    scope: { contentExp: '&', placement: '@', popupClass: '@', animation: '&', isOpen: '&',
      originScope: '&' },
    templateUrl: 'template/tooltip/tooltip-template-popup.html'
  };
})

.directive('tooltipTemplate', ['$tooltip', function($tooltip) {
  return $tooltip('tooltipTemplate', 'tooltip', 'mouseenter', {
    useContentExp: true
  });
}])

.directive('tooltipHtmlPopup', function() {
  return {
    restrict: 'EA',
    replace: true,
    scope: { contentExp: '&', placement: '@', popupClass: '@', animation: '&', isOpen: '&' },
    templateUrl: 'template/tooltip/tooltip-html-popup.html'
  };
})

.directive('tooltipHtml', ['$tooltip', function($tooltip) {
  return $tooltip('tooltipHtml', 'tooltip', 'mouseenter', {
    useContentExp: true
  });
}]);
<commit_msg>fix(tooltip): wrong position by a fraction of a second<commit_after>/**
 * The following features are still outstanding: animation as a
 * function, placement as a function, inside, support for more triggers than
 * just mouse enter/leave, html tooltips, and selector delegation.
 */
angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position'])

/**
 * The $tooltip service creates tooltip- and popover-like directives as well as
 * houses global options for them.
 */
.provider('$tooltip', function() {
  // The default options tooltip and popover.
  var defaultOptions = {
    placement: 'top',
    animation: true,
    popupDelay: 0,
    useContentExp: false
  };

  // Default hide triggers for each show trigger
  var triggerMap = {
    'mouseenter': 'mouseleave',
    'click': 'click',
    'focus': 'blur',
    'none': ''
  };

  // The options specified to the provider globally.
  var globalOptions = {};

  /**
   * `options({})` allows global configuration of all tooltips in the
   * application.
   *
   *   var app = angular.module( 'App', ['ui.bootstrap.tooltip'], function( $tooltipProvider ) {
   *     // place tooltips left instead of top by default
   *     $tooltipProvider.options( { placement: 'left' } );
   *   });
   */
	this.options = function(value) {
		angular.extend(globalOptions, value);
	};

  /**
   * This allows you to extend the set of trigger mappings available. E.g.:
   *
   *   $tooltipProvider.setTriggers( 'openTrigger': 'closeTrigger' );
   */
  this.setTriggers = function setTriggers(triggers) {
    angular.extend(triggerMap, triggers);
  };

  /**
   * This is a helper function for translating camel-case to snake-case.
   */
  function snake_case(name) {
    var regexp = /[A-Z]/g;
    var separator = '-';
    return name.replace(regexp, function(letter, pos) {
      return (pos ? separator : '') + letter.toLowerCase();
    });
  }

  /**
   * Returns the actual instance of the $tooltip service.
   * TODO support multiple triggers
   */
  this.$get = ['$window', '$compile', '$timeout', '$document', '$position', '$interpolate', '$rootScope', '$parse', function($window, $compile, $timeout, $document, $position, $interpolate, $rootScope, $parse) {
    return function $tooltip(type, prefix, defaultTriggerShow, options) {
      options = angular.extend({}, defaultOptions, globalOptions, options);

      /**
       * Returns an object of show and hide triggers.
       *
       * If a trigger is supplied,
       * it is used to show the tooltip; otherwise, it will use the `trigger`
       * option passed to the `$tooltipProvider.options` method; else it will
       * default to the trigger supplied to this directive factory.
       *
       * The hide trigger is based on the show trigger. If the `trigger` option
       * was passed to the `$tooltipProvider.options` method, it will use the
       * mapped trigger from `triggerMap` or the passed trigger if the map is
       * undefined; otherwise, it uses the `triggerMap` value of the show
       * trigger; else it will just use the show trigger.
       */
      function getTriggers(trigger) {
        var show = (trigger || options.trigger || defaultTriggerShow).split(' ');
        var hide = show.map(function(trigger) {
          return triggerMap[trigger] || trigger;
        });
        return {
          show: show,
          hide: hide
        };
      }

      var directiveName = snake_case(type);

      var startSym = $interpolate.startSymbol();
      var endSym = $interpolate.endSymbol();
      var template =
        '<div '+ directiveName +'-popup '+
          'title="'+startSym+'title'+endSym+'" '+
          (options.useContentExp ?
            'content-exp="contentExp()" ' :
            'content="'+startSym+'content'+endSym+'" ') +
          'placement="'+startSym+'placement'+endSym+'" '+
          'popup-class="'+startSym+'popupClass'+endSym+'" '+
          'animation="animation" '+
          'is-open="isOpen"'+
          'origin-scope="origScope" '+
          'style="visibility: hidden"'+
          '>'+
        '</div>';

      return {
        restrict: 'EA',
        compile: function(tElem, tAttrs) {
          var tooltipLinker = $compile( template );

          return function link(scope, element, attrs, tooltipCtrl) {
            var tooltip;
            var tooltipLinkedScope;
            var transitionTimeout;
            var popupTimeout;
            var positionTimeout;
            var appendToBody = angular.isDefined(options.appendToBody) ? options.appendToBody : false;
            var triggers = getTriggers(undefined);
            var hasEnableExp = angular.isDefined(attrs[prefix + 'Enable']);
            var ttScope = scope.$new(true);
            var repositionScheduled = false;
            var isOpenExp = angular.isDefined(attrs[prefix + 'IsOpen']) ? $parse(attrs[prefix + 'IsOpen']) : false;

            var positionTooltip = function() {
              // check if tooltip exists and is not empty
              if (!tooltip || !tooltip.html()) { return; }

              if (!positionTimeout) {
                positionTimeout = $timeout(function() {
                  // Reset the positioning and box size for correct width and height values.
                  tooltip.css({ top: 0, left: 0, width: 'auto', height: 'auto', visibility: 'hidden' });

                  var ttBox = $position.position(tooltip);
                  var ttCss = $position.positionElements(element, tooltip, ttScope.placement, appendToBody);
                  ttCss.top += 'px';
                  ttCss.left += 'px';

                  ttCss.width = ttBox.width + 'px';
                  ttCss.height = ttBox.height + 'px';

                  ttCss.visibility = 'visible';

                  // Now set the calculated positioning and size.
                  tooltip.css(ttCss);

                  positionTimeout = null;

                }, 0, false);
              }
            };

            // Set up the correct scope to allow transclusion later
            ttScope.origScope = scope;

            // By default, the tooltip is not open.
            // TODO add ability to start tooltip opened
            ttScope.isOpen = false;

            function toggleTooltipBind() {
              if (!ttScope.isOpen) {
                showTooltipBind();
              } else {
                hideTooltipBind();
              }
            }

            // Show the tooltip with delay if specified, otherwise show it immediately
            function showTooltipBind() {
              if (hasEnableExp && !scope.$eval(attrs[prefix + 'Enable'])) {
                return;
              }

              prepareTooltip();

              if (ttScope.popupDelay) {
                // Do nothing if the tooltip was already scheduled to pop-up.
                // This happens if show is triggered multiple times before any hide is triggered.
                if (!popupTimeout) {
                  popupTimeout = $timeout(show, ttScope.popupDelay, false);
                }
              } else {
                show();
              }
            }

            function hideTooltipBind () {
              hide();
              if (!$rootScope.$$phase) {
                $rootScope.$digest();
              }
            }

            // Show the tooltip popup element.
            function show() {
              popupTimeout = null;

              // If there is a pending remove transition, we must cancel it, lest the
              // tooltip be mysteriously removed.
              if (transitionTimeout) {
                $timeout.cancel(transitionTimeout);
                transitionTimeout = null;
              }

              // Don't show empty tooltips.
              if (!(options.useContentExp ? ttScope.contentExp() : ttScope.content)) {
                return angular.noop;
              }

              createTooltip();

              // And show the tooltip.
              ttScope.isOpen = true;
              if (isOpenExp) {
                isOpenExp.assign(ttScope.origScope, ttScope.isOpen);
              }

              if (!$rootScope.$$phase) {
                ttScope.$apply(); // digest required as $apply is not called
              }

              positionTooltip();
            }

            // Hide the tooltip popup element.
            function hide() {
              if (!ttScope) {
                return;
              }

              // First things first: we don't show it anymore.
              ttScope.isOpen = false;
              if (isOpenExp) {
                isOpenExp.assign(ttScope.origScope, ttScope.isOpen);
              }

              //if tooltip is going to be shown after delay, we must cancel this
              $timeout.cancel(popupTimeout);
              popupTimeout = null;

              $timeout.cancel(positionTimeout);
              positionTimeout = null;

              // And now we remove it from the DOM. However, if we have animation, we
              // need to wait for it to expire beforehand.
              // FIXME: this is a placeholder for a port of the transitions library.
              if (ttScope.animation) {
                if (!transitionTimeout) {
                  transitionTimeout = $timeout(removeTooltip, 500);
                }
              } else {
                removeTooltip();
              }
            }

            function createTooltip() {
              // There can only be one tooltip element per directive shown at once.
              if (tooltip) {
                removeTooltip();
              }
              tooltipLinkedScope = ttScope.$new();
              tooltip = tooltipLinker(tooltipLinkedScope, function(tooltip) {
                if (appendToBody) {
                  $document.find('body').append(tooltip);
                } else {
                  element.after(tooltip);
                }
              });

              if (options.useContentExp) {
                tooltipLinkedScope.$watch('contentExp()', function(val) {
                  if (!val && ttScope.isOpen) {
                    hide();
                  }
                });

                tooltipLinkedScope.$watch(function() {
                  if (!repositionScheduled) {
                    repositionScheduled = true;
                    tooltipLinkedScope.$$postDigest(function() {
                      repositionScheduled = false;
                      if (ttScope.isOpen) {
                        positionTooltip();
                      }
                    });
                  }
                });

              }
            }

            function removeTooltip() {
              transitionTimeout = null;
              if (tooltip) {
                tooltip.remove();
                tooltip = null;
              }
              if (tooltipLinkedScope) {
                tooltipLinkedScope.$destroy();
                tooltipLinkedScope = null;
              }
            }

            function prepareTooltip() {
              prepPopupClass();
              prepPlacement();
              prepPopupDelay();
            }

            ttScope.contentExp = function() {
              return scope.$eval(attrs[type]);
            };

            /**
             * Observe the relevant attributes.
             */
            if (!options.useContentExp) {
              attrs.$observe(type, function(val) {
                ttScope.content = val;

                if (!val && ttScope.isOpen) {
                  hide();
                } else {
                  positionTooltip();
                }
              });
            }

            attrs.$observe('disabled', function(val) {
              if (popupTimeout && val) {
                $timeout.cancel(popupTimeout);
                popupTimeout = null;
              }

              if (val && ttScope.isOpen) {
                hide();
              }
            });

            attrs.$observe(prefix + 'Title', function(val) {
              ttScope.title = val;
              positionTooltip();
            });

            attrs.$observe(prefix + 'Placement', function() {
              if (ttScope.isOpen) {
                prepPlacement();
                positionTooltip();
              }
            });

            if (isOpenExp) {
              scope.$watch(isOpenExp, function(val) {
                if (val !== ttScope.isOpen) {
                  toggleTooltipBind();
                }
              });
            }

            function prepPopupClass() {
              ttScope.popupClass = attrs[prefix + 'Class'];
            }

            function prepPlacement() {
              var val = attrs[prefix + 'Placement'];
              ttScope.placement = angular.isDefined(val) ? val : options.placement;
            }

            function prepPopupDelay() {
              var val = attrs[prefix + 'PopupDelay'];
              var delay = parseInt(val, 10);
              ttScope.popupDelay = !isNaN(delay) ? delay : options.popupDelay;
            }

            var unregisterTriggers = function() {
              triggers.show.forEach(function(trigger) {
                element.unbind(trigger, showTooltipBind);
              });
              triggers.hide.forEach(function(trigger) {
                element.unbind(trigger, hideTooltipBind);
              });
            };

            function prepTriggers() {
              var val = attrs[prefix + 'Trigger'];
              unregisterTriggers();

              triggers = getTriggers(val);

              if (triggers.show !== 'none') {
                triggers.show.forEach(function(trigger, idx) {
                  // Using raw addEventListener due to jqLite/jQuery bug - #4060
                  if (trigger === triggers.hide[idx]) {
                    element[0].addEventListener(trigger, toggleTooltipBind);
                  } else if (trigger) {
                    element[0].addEventListener(trigger, showTooltipBind);
                    element[0].addEventListener(triggers.hide[idx], hideTooltipBind);
                  }
                });
              }
            }
            prepTriggers();

            var animation = scope.$eval(attrs[prefix + 'Animation']);
            ttScope.animation = angular.isDefined(animation) ? !!animation : options.animation;

            var appendToBodyVal = scope.$eval(attrs[prefix + 'AppendToBody']);
            appendToBody = angular.isDefined(appendToBodyVal) ? appendToBodyVal : appendToBody;

            // if a tooltip is attached to <body> we need to remove it on
            // location change as its parent scope will probably not be destroyed
            // by the change.
            if (appendToBody) {
              scope.$on('$locationChangeSuccess', function closeTooltipOnLocationChangeSuccess() {
                if (ttScope.isOpen) {
                  hide();
                }
              });
            }

            // Make sure tooltip is destroyed and removed.
            scope.$on('$destroy', function onDestroyTooltip() {
              $timeout.cancel(transitionTimeout);
              $timeout.cancel(popupTimeout);
              $timeout.cancel(positionTimeout);
              unregisterTriggers();
              removeTooltip();
              ttScope = null;
            });
          };
        }
      };
    };
  }];
})

// This is mostly ngInclude code but with a custom scope
.directive('tooltipTemplateTransclude', [
         '$animate', '$sce', '$compile', '$templateRequest',
function ($animate ,  $sce ,  $compile ,  $templateRequest) {
  return {
    link: function(scope, elem, attrs) {
      var origScope = scope.$eval(attrs.tooltipTemplateTranscludeScope);

      var changeCounter = 0,
        currentScope,
        previousElement,
        currentElement;

      var cleanupLastIncludeContent = function() {
        if (previousElement) {
          previousElement.remove();
          previousElement = null;
        }
        if (currentScope) {
          currentScope.$destroy();
          currentScope = null;
        }
        if (currentElement) {
          $animate.leave(currentElement).then(function() {
            previousElement = null;
          });
          previousElement = currentElement;
          currentElement = null;
        }
      };

      scope.$watch($sce.parseAsResourceUrl(attrs.tooltipTemplateTransclude), function(src) {
        var thisChangeId = ++changeCounter;

        if (src) {
          //set the 2nd param to true to ignore the template request error so that the inner
          //contents and scope can be cleaned up.
          $templateRequest(src, true).then(function(response) {
            if (thisChangeId !== changeCounter) { return; }
            var newScope = origScope.$new();
            var template = response;

            var clone = $compile(template)(newScope, function(clone) {
              cleanupLastIncludeContent();
              $animate.enter(clone, elem);
            });

            currentScope = newScope;
            currentElement = clone;

            currentScope.$emit('$includeContentLoaded', src);
          }, function() {
            if (thisChangeId === changeCounter) {
              cleanupLastIncludeContent();
              scope.$emit('$includeContentError', src);
            }
          });
          scope.$emit('$includeContentRequested', src);
        } else {
          cleanupLastIncludeContent();
        }
      });

      scope.$on('$destroy', cleanupLastIncludeContent);
    }
  };
}])

/**
 * Note that it's intentional that these classes are *not* applied through $animate.
 * They must not be animated as they're expected to be present on the tooltip on
 * initialization.
 */
.directive('tooltipClasses', function() {
  return {
    restrict: 'A',
    link: function(scope, element, attrs) {
      if (scope.placement) {
        element.addClass(scope.placement);
      }
      if (scope.popupClass) {
        element.addClass(scope.popupClass);
      }
      if (scope.animation()) {
        element.addClass(attrs.tooltipAnimationClass);
      }
    }
  };
})

.directive('tooltipPopup', function() {
  return {
    restrict: 'EA',
    replace: true,
    scope: { content: '@', placement: '@', popupClass: '@', animation: '&', isOpen: '&' },
    templateUrl: 'template/tooltip/tooltip-popup.html'
  };
})

.directive('tooltip', [ '$tooltip', function($tooltip) {
  return $tooltip('tooltip', 'tooltip', 'mouseenter');
}])

.directive('tooltipTemplatePopup', function() {
  return {
    restrict: 'EA',
    replace: true,
    scope: { contentExp: '&', placement: '@', popupClass: '@', animation: '&', isOpen: '&',
      originScope: '&' },
    templateUrl: 'template/tooltip/tooltip-template-popup.html'
  };
})

.directive('tooltipTemplate', ['$tooltip', function($tooltip) {
  return $tooltip('tooltipTemplate', 'tooltip', 'mouseenter', {
    useContentExp: true
  });
}])

.directive('tooltipHtmlPopup', function() {
  return {
    restrict: 'EA',
    replace: true,
    scope: { contentExp: '&', placement: '@', popupClass: '@', animation: '&', isOpen: '&' },
    templateUrl: 'template/tooltip/tooltip-html-popup.html'
  };
})

.directive('tooltipHtml', ['$tooltip', function($tooltip) {
  return $tooltip('tooltipHtml', 'tooltip', 'mouseenter', {
    useContentExp: true
  });
}]);
<|endoftext|>


<commit_before>/*
 * Copyright 2021, TeamDev. All rights reserved.
 *
 * 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
 *
 * Redistribution and use in source and/or binary forms, with or without
 * modification, must retain the above copyright notice and the following
 * disclaimer.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/**
 * This script contains helper functions for switching between Java/Kotlin source code examples.
 *
 * The selected language will be saved in cookies so that the user can navigate between pages.
 *
 * To show tabs use this structure:
 * ```
 * <div class="code-tabs">
 *     <div class="code-tab-content java">
 *          Any Java content here
 *     </div>
 *     <div class="code-tab-content kotlin">
 *          Any Kotlin content here
 *     </div>
 * </div>
 * ```
 *
 * In markdown, all tags should be left-aligned. This way, the blocks of code
 * will not be broken:
 * ```
 * <div class="code-tabs">
 * <div class="code-tab-content java">
 * Any Java content here
 * </div>
 * <div class="code-tab-content kotlin">
 * Any Kotlin content here
 * </div>
 * </div>
 * ```
 *
 * If you don't need to display the tabs, but only need to show a specific paragraph of text
 * or change the title depending on the language, just use this:
 * ```
 * <div class="code-tab-content java">
 * # Getting started with Spine in Java
 * </div>
 * <div class="code-tab-content kotlin">
 * # Getting started with Spine in Kotlin
 * </div>
 * ```
 *
 * To change only some of the words in a sentence, use the `<span>` tag with the `.inline` class:
 * ```
 * A minimal client-server application in
 * <span class="code-tab-content inline java">Java</span>
 * <span class="code-tab-content inline kotlin">Kotlin</span>
 * which handles one command to print some text...
 * ```
 */

'use strict';

$(
    function() {
        const cookieCodeLang = 'codeLang';
        const $codeTabs = $('.code-tabs');
        const $codeTabContent = $('.code-tab-content');

        // TODO:2021-03-17:juliaevseeva: Update the logic to make it easy to add new programming languages.
        const primaryLang = 'java';
        const secondaryLang = 'kotlin';

        addTabSwitcher();
        initCodeLangSwitcher();

        /**
         * Adds a tab switcher to the DOM.
         */
        function addTabSwitcher() {
            $codeTabs.each(function() {
                const tabBlock = $(this);
                createTab(tabBlock, createTabContainer(tabBlock));
            });
        }

        /**
         * Creates a container for tabs.
         *
         * @param tabBlock a block that contains tabs.
         * @return {jQuery|HTMLElement} tabContainer a container for tabs.
         */
        function createTabContainer(tabBlock) {
            const tabContainer = $('

');
            tabBlock.prepend(tabContainer);
            return tabContainer;
        }

        /**
         * Creates a tab inside the container for each `code-tab-content` element.
         *
         * @param tabBlock a block that contains tabs.
         * @param tabContainer a container for tabs.
         */
        function createTab(tabBlock, tabContainer) {
            const tabContent = tabBlock.find($codeTabContent);
            tabContent.each(function () {
                const tabName = $(this).attr('class').split(' ')[1];
                const item = $(`<div class="tab ${tabName}">${tabName}</div>`);
                tabContainer.append(item);
            });
        }

        /**
         * Inits a code language switcher.
         *
         * By default, sets the primary code language to the `cookie`.
         * On a tab click switches between code languages.
         */
        function initCodeLangSwitcher() {
            const cookieValue = Cookies.get(cookieCodeLang);

            if (cookieValue == null) {
                setCodeLang(primaryLang);
            } else {
                primaryLang === cookieValue && setCodeLang(primaryLang);
                secondaryLang === cookieValue && setCodeLang(secondaryLang);
            }

            $('.tab').click(function() {
                const lang = $(this).attr('class').split(' ')[1];

                if (lang === primaryLang) {
                    setCodeLang(primaryLang);
                } else {
                    setCodeLang(secondaryLang);
                }
            });
        }

        /**
         * Sets the chosen code language to the `cookie` and adds corresponding
         * CSS classes to the selected tab and content element.
         *
         * The CSS file is located at `_sass/modules/_code-tabs.scss`.
         *
         * @param codeLang a selected code language.
         */
        function setCodeLang(codeLang) {
            Cookies.set(cookieCodeLang, codeLang);
            $('.tab').removeClass('selected');
            $('.tab.' + codeLang).addClass('selected');
            $codeTabContent.removeClass('show');
            $('.code-tab-content.' + codeLang).addClass('show');
        }
    }
);
<commit_msg>Make it possible to add as many code languages as we want<commit_after>/*
 * Copyright 2021, TeamDev. All rights reserved.
 *
 * 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
 *
 * Redistribution and use in source and/or binary forms, with or without
 * modification, must retain the above copyright notice and the following
 * disclaimer.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/**
 * This script contains helper functions for switching between Java/Kotlin source code examples.
 *
 * The selected language will be saved in cookies so that the user can navigate between pages.
 *
 * To show tabs use the structure below.
 * Add corresponding `.java`, `.kotlin`, or any other language class for `div`s with content.
 * If no language is provided, the content will not be displayed.
 * ```
 * <div class="code-tabs">
 *     <div class="code-tab-content java">
 *          Any Java content here
 *     </div>
 *     <div class="code-tab-content kotlin">
 *          Any Kotlin content here
 *     </div>
 * </div>
 * ```
 *
 * In markdown, all tags should be left-aligned. This way, the blocks of code
 * will not be broken:
 * ```
 * <div class="code-tabs">
 * <div class="code-tab-content java">
 * Any Java content here
 * </div>
 * <div class="code-tab-content kotlin">
 * Any Kotlin content here
 * </div>
 * </div>
 * ```
 *
 * If you don't need to display the tabs, but only need to show a specific paragraph of text
 * or change the title depending on the language, just use this:
 * ```
 * <div class="code-tab-content java">
 * # Getting started with Spine in Java
 * </div>
 * <div class="code-tab-content kotlin">
 * # Getting started with Spine in Kotlin
 * </div>
 * ```
 *
 * To change only some of the words in a sentence, use the `<span>` tag with the `.inline` class:
 * ```
 * A minimal client-server application in
 * <span class="code-tab-content inline java">Java</span>
 * <span class="code-tab-content inline kotlin">Kotlin</span>
 * which handles one command to print some text...
 * ```
 */

'use strict';

$(
    function() {
        const cookieCodeLang = 'codeLang';
        const $codeTabs = $('.code-tabs');
        const $codeTabContent = $('.code-tab-content');

        addTabSwitcher();
        initCodeLangSwitcher();

        /**
         * Adds a tab switcher to the DOM.
         */
        function addTabSwitcher() {
            $codeTabs.each(function() {
                const tabBlock = $(this);
                createTab(tabBlock, createTabContainer(tabBlock));
            });
        }

        /**
         * Creates a container for tabs.
         *
         * @param tabBlock a block that contains tabs.
         * @return {jQuery|HTMLElement} tabContainer a container for tabs.
         */
        function createTabContainer(tabBlock) {
            const tabContainer = $('

');
            tabBlock.prepend(tabContainer);
            return tabContainer;
        }

        /**
         * Creates a tab inside the container for each `code-tab-content` element.
         *
         * @param tabBlock a block that contains tabs.
         * @param tabContainer a container for tabs.
         */
        function createTab(tabBlock, tabContainer) {
            const tabContent = tabBlock.find($codeTabContent);
            tabContent.each(function () {
                const tabName = $(this).attr('class').split(' ')[1];
                if (typeof tabName !== 'undefined') {
                    const item = $(`<div class="tab ${tabName}">${tabName}</div>`);
                    tabContainer.append(item);
                }
            });
        }

        /**
         * Inits a code language switcher.
         *
         * By default, sets the primary code language to the `cookie`.
         * On a tab click switches between code languages.
         */
        function initCodeLangSwitcher() {
            const primaryLang = 'java';
            const cookieValue = Cookies.get(cookieCodeLang);

            if (cookieValue == null) {
                setCodeLang(primaryLang);
            } else {
                setCodeLang(cookieValue);
            }

            $('.tab').click(function() {
                const lang = $(this).attr('class').split(' ')[1];
                if (typeof lang !== 'undefined') {
                    setCodeLang(lang);
                }
            });
        }

        /**
         * Sets the chosen code language to the `cookie` and adds corresponding
         * CSS classes to the selected tab and content element.
         *
         * The CSS file is located at `_sass/modules/_code-tabs.scss`.
         *
         * @param codeLang a selected code language.
         */
        function setCodeLang(codeLang) {
            Cookies.set(cookieCodeLang, codeLang);
            $('.tab').removeClass('selected');
            $('.tab.' + codeLang).addClass('selected');
            $codeTabContent.removeClass('show');
            $('.code-tab-content.' + codeLang).addClass('show');
        }
    }
);
<|endoftext|>


<commit_before>const { parseUrl } = require('mysql/lib/ConnectionConfig');
const Profiler = require('../server/profiler.js');
const Logger = require('../server/logger.js');
const MySQL = require('../server/mysql.js');
const {
  typeCast, safeInvoke, prepareLegacyQuery, sanitizeInput,
} = require('../server/utils.js');

const logger = new Logger(global.GetConvar('mysql_debug_output', 'console'));
const profiler = new Profiler(logger, {
  trace: global.GetConvarInt('mysql_debug', 0),
  slowQueryWarningTime: global.GetConvarInt('mysql_slow_query_warning', 100),
});

const config = JSON.parse(global.LoadResourceFile('ghmattimysql', 'config.json'));
const configFromString = parseUrl(global.GetConvar('mysql_connection_string', 'mysql://localhost/fivem'));

const mysql = new MySQL(config || configFromString, logger, profiler);

const currentResourceName = global.GetCurrentResourceName();

global.exports('scalar', (query, parameters, callback, resource) => {
  const invokingResource = resource || global.GetInvokingResource();
  let sql = query;
  let values = parameters;
  let cb = callback;
  [sql, values, cb] = sanitizeInput(sql, values, cb);

  mysql.execute({ sql, values, typeCast }, invokingResource).then((result) => {
    safeInvoke(cb, (result && result[0]) ? Object.values(result[0])[0] : null);
  }).catch(() => {});
});

global.exports('execute', (query, parameters, callback, resource) => {
  const invokingResource = resource || global.GetInvokingResource();
  let sql = query;
  let values = parameters;
  let cb = callback;
  [sql, values, cb] = sanitizeInput(sql, values, cb);

  mysql.execute({ sql, values, typeCast }, invokingResource).then((result) => {
    safeInvoke(cb, result);
  }).catch(() => {});
});

function onTransactionError(error, connection, callback) {
  connection.rollback(() => {
    logger.error(error.message);
    safeInvoke(callback, false);
  });
}

global.exports('transaction', (querys, parameters, callback, resource) => {
  const invokingResource = resource || global.GetInvokingResource();
  let sqls = [];
  let params = parameters;
  let cb = callback;
  // start by type-checking and sorting the data
  if (!querys.every(element => typeof element === 'string')) {
    sqls = querys;
    if (typeof parameters === 'function') cb = parameters;
  } else {
    if (typeof parameters === 'function') {
      cb = parameters;
      params = [];
    }
    querys.forEach((element) => {
      sqls.push({ query: element, parameters: params });
    });
  }
  // build the actual queries
  sqls.forEach((element, index) => {
    const [stmt, stmtParams] = prepareLegacyQuery(element.query, element.parameters);
    sqls[index] = {
      query: stmt,
      parameters: (Array.isArray(stmtParams)) ? stmtParams : [],
    };
  });
  // the real transaction can begin
  mysql.pool.getConnection((connectionError, connection) => {
    if (connectionError) {
      logger.error(connectionError.message);
      safeInvoke(cb, false);
      return;
    }
    connection.beginTransaction((transactionError) => {
      if (transactionError) {
        onTransactionError(transactionError, connection, callback);
        return;
      }
      const promises = [];
      // execute each query on the connection
      sqls.forEach((element) => {
        promises.push(mysql.execute({
          sql: element.query,
          values: element.parameters,
        }, invokingResource, connection));
      });
      // If all have resolved, then commit
      Promise.all(promises).then(() => {
        connection.commit((commitError) => {
          if (commitError) {
            onTransactionError(commitError, connection, callback);
          } else safeInvoke(cb, true);
        });
        // Otherwise catch the error from the execution
      }).catch((executeError) => {
        onTransactionError(executeError, connection, callback);
      });
    });
  });
});

global.onNet(`${currentResourceName}:request-data`, () => {
  const src = global.source;
  global.emitNet(`${currentResourceName}:update-resource-data`, src, profiler.profiles.resources);
  global.emitNet(`${currentResourceName}:update-time-data`, src, profiler.profiles.executionTimes);
  global.emitNet(`${currentResourceName}:update-slow-queries`, src, profiler.profiles.slowQueries);
});
<commit_msg>return and make sure catch is not empty.<commit_after>const { parseUrl } = require('mysql/lib/ConnectionConfig');
const Profiler = require('../server/profiler.js');
const Logger = require('../server/logger.js');
const MySQL = require('../server/mysql.js');
const {
  typeCast, safeInvoke, prepareLegacyQuery, sanitizeInput,
} = require('../server/utils.js');

const logger = new Logger(global.GetConvar('mysql_debug_output', 'console'));
const profiler = new Profiler(logger, {
  trace: global.GetConvarInt('mysql_debug', 0),
  slowQueryWarningTime: global.GetConvarInt('mysql_slow_query_warning', 100),
});

const config = JSON.parse(global.LoadResourceFile('ghmattimysql', 'config.json'));
const configFromString = parseUrl(global.GetConvar('mysql_connection_string', 'mysql://localhost/fivem'));

const mysql = new MySQL(config || configFromString, logger, profiler);

const currentResourceName = global.GetCurrentResourceName();

global.exports('scalar', (query, parameters, callback, resource) => {
  const invokingResource = resource || global.GetInvokingResource();
  let sql = query;
  let values = parameters;
  let cb = callback;
  [sql, values, cb] = sanitizeInput(sql, values, cb);

  mysql.execute({ sql, values, typeCast }, invokingResource).then((result) => {
    safeInvoke(cb, (result && result[0]) ? Object.values(result[0])[0] : null);
    return true;
  }).catch(() => false);
});

global.exports('execute', (query, parameters, callback, resource) => {
  const invokingResource = resource || global.GetInvokingResource();
  let sql = query;
  let values = parameters;
  let cb = callback;
  [sql, values, cb] = sanitizeInput(sql, values, cb);

  mysql.execute({ sql, values, typeCast }, invokingResource).then((result) => {
    safeInvoke(cb, result);
    return true;
  }).catch(() => false);
});

function onTransactionError(error, connection, callback) {
  connection.rollback(() => {
    logger.error(error.message);
    safeInvoke(callback, false);
  });
}

global.exports('transaction', (querys, parameters, callback, resource) => {
  const invokingResource = resource || global.GetInvokingResource();
  let sqls = [];
  let params = parameters;
  let cb = callback;
  // start by type-checking and sorting the data
  if (!querys.every(element => typeof element === 'string')) {
    sqls = querys;
    if (typeof parameters === 'function') cb = parameters;
  } else {
    if (typeof parameters === 'function') {
      cb = parameters;
      params = [];
    }
    querys.forEach((element) => {
      sqls.push({ query: element, parameters: params });
    });
  }
  // build the actual queries
  sqls.forEach((element, index) => {
    const [stmt, stmtParams] = prepareLegacyQuery(element.query, element.parameters);
    sqls[index] = {
      query: stmt,
      parameters: (Array.isArray(stmtParams)) ? stmtParams : [],
    };
  });
  // the real transaction can begin
  mysql.pool.getConnection((connectionError, connection) => {
    if (connectionError) {
      logger.error(connectionError.message);
      safeInvoke(cb, false);
      return;
    }
    connection.beginTransaction((transactionError) => {
      if (transactionError) {
        onTransactionError(transactionError, connection, callback);
        return;
      }
      const promises = [];
      // execute each query on the connection
      sqls.forEach((element) => {
        promises.push(mysql.execute({
          sql: element.query,
          values: element.parameters,
        }, invokingResource, connection));
      });
      // If all have resolved, then commit
      Promise.all(promises).then(() => {
        connection.commit((commitError) => {
          if (commitError) {
            onTransactionError(commitError, connection, callback);
          } else safeInvoke(cb, true);
        });
        // Otherwise catch the error from the execution
      }).catch((executeError) => {
        onTransactionError(executeError, connection, callback);
      });
    });
  });
});

global.onNet(`${currentResourceName}:request-data`, () => {
  const src = global.source;
  global.emitNet(`${currentResourceName}:update-resource-data`, src, profiler.profiles.resources);
  global.emitNet(`${currentResourceName}:update-time-data`, src, profiler.profiles.executionTimes);
  global.emitNet(`${currentResourceName}:update-slow-queries`, src, profiler.profiles.slowQueries);
});
<|endoftext|>