<commit_before>var _ = require("lodash");
var path = require("path");
var fs = require("fs");
var Bacon = require("baconjs");
var nodegit = require("nodegit");
var autocomplete = require("cliparse").autocomplete;
var Promise = require("bluebird");

var Logger = require("../logger.js");

module.exports = function(repositoryPath) {

  var Git = {};
  Git.GIT_BRANCH_LOCAL = 1;

  Git.fetchOptions = Git.pushOptions = {
    callbacks: {
      certificateCheck: function() { return 1; },
      credentials: function(url, username) {
        return nodegit.Cred.sshKeyFromAgent(username);
      }
    }
  };

  Git.getRepository = function(candidatePath) {
    if(typeof candidatePath == "undefined") {
      candidatePath = repositoryPath;
    }
    Logger.debug("Open the local repository… (from " + candidatePath + ")");
    var s_repository = Bacon.fromNodeCallback(
      _.partial(fs.stat, path.join(candidatePath, ".git"))
    ).flatMapLatest(function(stats) {
      if(stats.isDirectory()) {
        return Bacon.fromPromise(nodegit.Repository.open(path.join(candidatePath, ".git")));
      } else {
        return new Bacon.Error("No repository found in " + candidatePath);
      }
    });

    return s_repository.flatMapError(function(error) {
      Logger.info("Cannot open repository in " + candidatePath + " (error: " + error + ")");
      if(path.parse(candidatePath).root == candidatePath) {
        return new Bacon.Error("No repository found");
      } else {
        return Git.getRepository(path.normalize(path.join(candidatePath, "..")));
      }
    }).toProperty();
  };

  Git.getRemote = function(name) {
    return Git.getRepository().flatMapLatest(function(repository) {
      Logger.debug("Load the \"" + name + "\" remote…");
      return Bacon.fromPromise(nodegit.Remote.lookup(repository, name));
    });
  };

  Git.getCurrentBranch = function() {
    return Git.getRepository().flatMapLatest(function(repository) {
      return Bacon.fromPromise(repository.getCurrentBranch());
    });
  };

  Git.getBranch = function(name) {
    return Git.getRepository().flatMapLatest(function(repository) {
      return Bacon.fromPromise(nodegit.Branch.lookup(repository, name, Git.GIT_BRANCH_LOCAL));
    }).toProperty();
  };

  Git.getBranches = function() {
    return Git.getRepository().flatMapLatest(function(repository) {
      return Bacon.fromPromise(repository.getReferences())
      .map(function(refs) {
        return _.map(_.filter(refs, function(ref) {
          return ref.isBranch();
        }), function(ref) { return ref.shorthand(); });
      });
    }).toProperty();
  };

  Git.completeBranches = function() {
    return Git.getBranches().firstToPromise(Promise).then(autocomplete.words);
  };

  Git.createRemote = function(name, remoteUrl) {
    // Replace git+ssh:// by ssh://, otherwise we get a "Malformed URL" error by nodegit
    var url = remoteUrl.replace(/^git\+/, "");

    var s_existingRemote = Git.getRemote(name);

    var s_existingValidRemote = s_existingRemote.skipErrors().flatMapLatest(function(remote) {
      Logger.debug("Check that the current \"" + name + "\" remote point to the right URL…");
      return remote.url() == url ? Bacon.once(remote) : new Bacon.Error("The \"" + name + "\" remote already exist and does not point to " + url);
    });

    // Create a remote only if it does not already exist
    var s_newRemote = s_existingRemote.errors().flatMapError(function() {
      Logger.debug("Create a \"" + name + "\" remote pointing to " + url);
      return Git.getRepository().flatMapLatest(function(repository) {
        Bacon.fromPromise(nodegit.Remote.create(repository, name, url));
        Logger.debug("Created remote " + name);
        return Git.getRemote(name);
      });
    });

    return Bacon.mergeAll(s_existingValidRemote, s_newRemote);
  };

  Git.fetch = function(remote) {
    Logger.debug("Fetching " + remote.name() + "…");
    return Bacon.fromPromise(
      remote.fetch([remote.name()], Git.fetchOptions)
    ).map(remote);
  };

  Git.keepFetching = function(timeout, remote) {
    var s_timeout = timeout > 0 ? Bacon.later(timeout, {}) : Bacon.never();
    var fetch = function() {
      Logger.debug("Fetching " + remote.name());
      return Bacon.fromPromise(
        remote.fetch(["refs/heads/master"], Git.fetchOptions)
      ).map(remote).flatMapError(function(error) {
        return (error.message != "Early EOF") ? new Bacon.Error(error) : Bacon.later(10000, {}).flatMapLatest(function() {
          return fetch();
        });
      });
    };

    Logger.debug("Wait for " + remote.name() + " to be fetchable…");
    var s_fetch = fetch().takeUntil(s_timeout).doAction(function() {
      Logger.debug("Fetched " + remote.name());
    });

    return s_fetch;
  };

  Git.getRemoteCommitId = function(remoteName) {
    return Git.getRepository().flatMapLatest(function(repo) {
      return Bacon.fromPromise(repo.getReferenceCommit(remoteName + '/master'))
            .map(function(commit) {
              return commit.id().toString();
            })
            .mapError(function(e) {
              return null;
            });
    });
  };

  Git.getCommitId = function(branchName) {
    var s_branch = branchName == "" ? Git.getCurrentBranch() : Git.getBranch(branchName);

    return Git.getRepository().flatMapLatest(function(repo) {
      return s_branch.flatMapLatest(function(branch) {
        return Bacon.fromPromise(repo.getBranchCommit(branch.name()))
              .map(function(commit) {
                return commit.id().toString();
              });
      });
    });
  };

  Git.push = function(remote, branch, s_commitId, force) {
    Logger.debug("Prepare the push…");

    var forcePush = force ? '+' : '';
    var s_current_branch = Git.getCurrentBranch();
    var s_branch = branch == "" ? s_current_branch : Git.getBranch(branch);

    return s_branch.flatMapLatest(function(branch) {

      return s_commitId.flatMapLatest(function(commitIdToPush) {
        return Git.getRemoteCommitId(remote.name()).flatMapLatest(function(remoteCommitId) {
          if(remoteCommitId != commitIdToPush) {
            Logger.debug("Preparing the push");
            return Bacon.fromPromise(
              remote.push([forcePush + branch + ":refs/heads/master"], Git.pushOptions)
            );
          } else {
            return new Bacon.Error("Nothing to push");
          }
        });
      });
    });
  };

  return Git;
};
<commit_msg>Wait for the remote to be created before getting it<commit_after>var _ = require("lodash");
var path = require("path");
var fs = require("fs");
var Bacon = require("baconjs");
var nodegit = require("nodegit");
var autocomplete = require("cliparse").autocomplete;
var Promise = require("bluebird");

var Logger = require("../logger.js");

module.exports = function(repositoryPath) {

  var Git = {};
  Git.GIT_BRANCH_LOCAL = 1;

  Git.fetchOptions = Git.pushOptions = {
    callbacks: {
      certificateCheck: function() { return 1; },
      credentials: function(url, username) {
        return nodegit.Cred.sshKeyFromAgent(username);
      }
    }
  };

  Git.getRepository = function(candidatePath) {
    if(typeof candidatePath == "undefined") {
      candidatePath = repositoryPath;
    }
    Logger.debug("Open the local repository… (from " + candidatePath + ")");
    var s_repository = Bacon.fromNodeCallback(
      _.partial(fs.stat, path.join(candidatePath, ".git"))
    ).flatMapLatest(function(stats) {
      if(stats.isDirectory()) {
        return Bacon.fromPromise(nodegit.Repository.open(path.join(candidatePath, ".git")));
      } else {
        return new Bacon.Error("No repository found in " + candidatePath);
      }
    });

    return s_repository.flatMapError(function(error) {
      Logger.info("Cannot open repository in " + candidatePath + " (error: " + error + ")");
      if(path.parse(candidatePath).root == candidatePath) {
        return new Bacon.Error("No repository found");
      } else {
        return Git.getRepository(path.normalize(path.join(candidatePath, "..")));
      }
    }).toProperty();
  };

  Git.getRemote = function(name) {
    return Git.getRepository().flatMapLatest(function(repository) {
      Logger.debug("Load the \"" + name + "\" remote…");
      return Bacon.fromPromise(nodegit.Remote.lookup(repository, name));
    });
  };

  Git.getCurrentBranch = function() {
    return Git.getRepository().flatMapLatest(function(repository) {
      return Bacon.fromPromise(repository.getCurrentBranch());
    });
  };

  Git.getBranch = function(name) {
    return Git.getRepository().flatMapLatest(function(repository) {
      return Bacon.fromPromise(nodegit.Branch.lookup(repository, name, Git.GIT_BRANCH_LOCAL));
    }).toProperty();
  };

  Git.getBranches = function() {
    return Git.getRepository().flatMapLatest(function(repository) {
      return Bacon.fromPromise(repository.getReferences())
      .map(function(refs) {
        return _.map(_.filter(refs, function(ref) {
          return ref.isBranch();
        }), function(ref) { return ref.shorthand(); });
      });
    }).toProperty();
  };

  Git.completeBranches = function() {
    return Git.getBranches().firstToPromise(Promise).then(autocomplete.words);
  };

  Git.createRemote = function(name, remoteUrl) {
    // Replace git+ssh:// by ssh://, otherwise we get a "Malformed URL" error by nodegit
    var url = remoteUrl.replace(/^git\+/, "");

    var s_existingRemote = Git.getRemote(name);

    var s_existingValidRemote = s_existingRemote.skipErrors().flatMapLatest(function(remote) {
      Logger.debug("Check that the current \"" + name + "\" remote point to the right URL…");
      return remote.url() == url ? Bacon.once(remote) : new Bacon.Error("The \"" + name + "\" remote already exist and does not point to " + url);
    });

    // Create a remote only if it does not already exist
    var s_newRemote = s_existingRemote.errors().flatMapError(function() {
      Logger.debug("Create a \"" + name + "\" remote pointing to " + url);
      return Git.getRepository().flatMapLatest(function(repository) {
        Logger.debug("Created remote " + name);
        return Bacon.fromPromise(nodegit.Remote.create(repository, name, url));
      }).flatMapLatest(function(){
        return Git.getRemote(name);
      });
    });

    return Bacon.mergeAll(s_existingValidRemote, s_newRemote);
  };

  Git.fetch = function(remote) {
    Logger.debug("Fetching " + remote.name() + "…");
    return Bacon.fromPromise(
      remote.fetch([remote.name()], Git.fetchOptions)
    ).map(remote);
  };

  Git.keepFetching = function(timeout, remote) {
    var s_timeout = timeout > 0 ? Bacon.later(timeout, {}) : Bacon.never();
    var fetch = function() {
      Logger.debug("Fetching " + remote.name());
      return Bacon.fromPromise(
        remote.fetch(["refs/heads/master"], Git.fetchOptions)
      ).map(remote).flatMapError(function(error) {
        return (error.message != "Early EOF") ? new Bacon.Error(error) : Bacon.later(10000, {}).flatMapLatest(function() {
          return fetch();
        });
      });
    };

    Logger.debug("Wait for " + remote.name() + " to be fetchable…");
    var s_fetch = fetch().takeUntil(s_timeout).doAction(function() {
      Logger.debug("Fetched " + remote.name());
    });

    return s_fetch;
  };

  Git.getRemoteCommitId = function(remoteName) {
    return Git.getRepository().flatMapLatest(function(repo) {
      return Bacon.fromPromise(repo.getReferenceCommit(remoteName + '/master'))
            .map(function(commit) {
              return commit.id().toString();
            })
            .mapError(function(e) {
              return null;
            });
    });
  };

  Git.getCommitId = function(branchName) {
    var s_branch = branchName == "" ? Git.getCurrentBranch() : Git.getBranch(branchName);

    return Git.getRepository().flatMapLatest(function(repo) {
      return s_branch.flatMapLatest(function(branch) {
        return Bacon.fromPromise(repo.getBranchCommit(branch.name()))
              .map(function(commit) {
                return commit.id().toString();
              });
      });
    });
  };

  Git.push = function(remote, branch, s_commitId, force) {
    Logger.debug("Prepare the push…");

    var forcePush = force ? '+' : '';
    var s_current_branch = Git.getCurrentBranch();
    var s_branch = branch == "" ? s_current_branch : Git.getBranch(branch);

    return s_branch.flatMapLatest(function(branch) {

      return s_commitId.flatMapLatest(function(commitIdToPush) {
        return Git.getRemoteCommitId(remote.name()).flatMapLatest(function(remoteCommitId) {
          if(remoteCommitId != commitIdToPush) {
            Logger.debug("Preparing the push");
            return Bacon.fromPromise(
              remote.push([forcePush + branch + ":refs/heads/master"], Git.pushOptions)
            );
          } else {
            return new Bacon.Error("Nothing to push");
          }
        });
      });
    });
  };

  return Git;
};
<|endoftext|>


<commit_before>// Copyright (C) 2013:
//    Alex Russell <slightlyoff@chromium.org>
//    Yehuda Katz
//
// Use of this source code is governed by
//    http://www.apache.org/licenses/LICENSE-2.0

// FIXME(slightlyoff):
//    - Document "npm test"
//    - Change global name from "Promise" to something less conflicty
(function(global, browserGlobal, underTest) {
"use strict";

// FIXME(slighltyoff):
//  * aggregates + tests
//  * check on fast-forwarding

underTest = !!underTest;

//
// Async Utilities
//

// Borrowed from RSVP.js
var async;

var MutationObserver = browserGlobal.MutationObserver ||
                       browserGlobal.WebKitMutationObserver;
var Promise;

if (typeof process !== 'undefined' &&
  {}.toString.call(process) === '[object process]') {
  async = function(callback, binding) {
    process.nextTick(function() {
      callback.call(binding);
    });
  };
} else if (MutationObserver) {
  var queue = [];

  var observer = new MutationObserver(function() {
    var toProcess = queue.slice();
    queue = [];
    toProcess.forEach(function(tuple) {
      tuple[0].call(tuple[1]);
    });
  });

  var element = document.createElement('div');
  observer.observe(element, { attributes: true });

  // Chrome Memory Leak: https://bugs.webkit.org/show_bug.cgi?id=93661
  window.addEventListener('unload', function(){
    observer.disconnect();
    observer = null;
  });

  async = function(callback, binding) {
    queue.push([callback, binding]);
    element.setAttribute('drainQueue', 'drainQueue');
  };
} else {
  async = function(callback, binding) {
    setTimeout(function() {
      callback.call(binding);
    }, 1);
  };
}

//
// Object Model Utilities
//

// defineProperties utilities
var _readOnlyProperty = function(v) {
    return {
      enumerable: true,
      configurable: false,
      get: v
    };
};

var _method = function(v, e, c, w) {
    return {
      enumerable:   !!(e || 0),
      configurable: !!(c || 1),
      writable:     !!(w || 1),
      value:           v || function() {}
    };
};

var _pseudoPrivate = function(v) { return _method(v, 0, 1, 0); };
var _public = function(v) { return _method(v, 1); };

//
// Promises Utilities
//

var isThenable = function(any) {
  if (any === undefined)
    return false;
  try {
    var f = any.then;
    if (typeof f == "function") {
      return true;
    }
  } catch (e) { /*squelch*/ }
  return false;
};

var AlreadyResolved = function(name) {
  Error.call(this, name);
};
AlreadyResolved.prototype = Object.create(Error.prototype);

var Backlog = function() {
  var bl = [];
  bl.pump = function(value) {
    async(function() {
      var l = bl.length;
      var x = 0;
      while(x < l) {
        x++;
        bl.shift()(value);
      }
    });
  };
  return bl;
};

//
// Resolver Constuctor
//

var Resolver = function(future,
                        fulfillCallbacks,
                        rejectCallbacks,
                        setValue,
                        setError,
                        setState) {
  var isResolved = false;

  var resolver = this;
  var fulfill = function(value) {
    // console.log("queueing fulfill with:", value);
    async(function() {
      setState("fulfilled");
      setValue(value);
      // console.log("fulfilling with:", value);
      fulfillCallbacks.pump(value);
    });
  };
  var reject = function(reason) {
    // console.log("queuing reject with:", reason);
    async(function() {
      setState("rejected");
      setError(reason);
      // console.log("rejecting with:", reason);
      rejectCallbacks.pump(reason);
    });
  };
  var resolve = function(value) {
    if (isThenable(value)) {
      value.then(resolve, reject);
      return;
    }
    fulfill(value);
  };
  var ifNotResolved = function(func, name) {
    return function(value) {
      if (!isResolved) {
        isResolved = true;
        func(value);
      } else {
        if (typeof console != "undefined") {
          console.error("Cannot resolve a Promise multiple times.");
        }
      }
    };
  };

  // Indirectly resolves the Promise, chaining any passed Promise's resolution
  this.resolve = ifNotResolved(resolve, "resolve");

  // Directly fulfills the future, no matter what value's type is
  this.fulfill = ifNotResolved(fulfill, "fulfill");

  // Rejects the future
  this.reject = ifNotResolved(reject, "reject");

  this.cancel  = function() { resolver.reject(new Error("Cancel")); };
  this.timeout = function() { resolver.reject(new Error("Timeout")); };

  if (underTest) {
    Object.defineProperties(this, {
      _isResolved: _readOnlyProperty(function() { return isResolved; }),
    });
  }

  setState("pending");
};

//
// Promise Constuctor
//

var Promise = function(init) {
  var fulfillCallbacks = new Backlog();
  var rejectCallbacks = new Backlog();
  var value;
  var error;
  var state = "pending";

  if (underTest) {
    Object.defineProperties(this, {
      _value: _readOnlyProperty(function() { return value; }),
      _error: _readOnlyProperty(function() { return error; }),
      _state: _readOnlyProperty(function() { return state; }),
    });
  }

  Object.defineProperties(this, {
    _addAcceptCallback: _pseudoPrivate(
      function(cb) {
        // console.log("adding fulfill callback:", cb);
        fulfillCallbacks.push(cb);
        if (state == "fulfilled") {
          fulfillCallbacks.pump(value);
        }
      }
    ),
    _addRejectCallback: _pseudoPrivate(
      function(cb) {
        // console.log("adding reject callback:", cb);
        rejectCallbacks.push(cb);
        if (state == "rejected") {
          rejectCallbacks.pump(error);
        }
      }
    ),
  });
  var r = new Resolver(this,
                       fulfillCallbacks, rejectCallbacks,
                       function(v) { value = v; },
                       function(e) { error = e; },
                       function(s) { state = s; })
  try {
    if (init) { init(r); }
  } catch(e) {
    r.reject(e);
  }
};

//
// Consructor
//

var isCallback = function(any) {
  return (typeof any == "function");
};

// Used in .then()
var wrap = function(callback, resolver, disposition) {
  if (!isCallback(callback)) {
    // If we don't get a callback, we want to forward whatever resolution we get
    return resolver[disposition].bind(resolver);
  }

  return function() {
    try {
      var r = callback.apply(null, arguments);
      resolver.resolve(r);
    } catch(e) {
      // Exceptions reject the resolver
      resolver.reject(e);
    }
  };
};

var addCallbacks = function(onfulfill, onreject, scope) {
  if (isCallback(onfulfill)) {
    scope._addAcceptCallback(onfulfill);
  }
  if (isCallback(onreject)) {
    scope._addRejectCallback(onreject);
  }
  return scope;
};

//
// Prototype properties
//

Promise.prototype = Object.create(null, {
  "then": _public(function(onfulfill, onreject) {
    // The logic here is:
    //    We return a new Promise whose resolution merges with the return from
    //    onfulfill() or onerror(). If onfulfill() returns a Promise, we forward
    //    the resolution of that future to the resolution of the returned
    //    Promise.
    var f = this;
    return new Promise(function(r) {
      addCallbacks(wrap(onfulfill, r, "resolve"),
                   wrap(onreject, r, "reject"), f);
    });
  }),
  "catch": _public(function(onreject) {
    var f = this;
    return new Promise(function(r) {
      addCallbacks(null, wrap(onreject, r, "reject"), f);
    });
  }),
});

//
// Statics
//

Promise.isThenable = isThenable;

var toPromiseList = function(list) {
  return Array.prototype.slice.call(list).map(Promise.resolve);
};

Promise.any = function(/*...futuresOrValues*/) {
  var futures = toPromiseList(arguments);
  return new Promise(function(r) {
    if (!futures.length) {
      r.reject("No futures passed to Promise.any()");
    } else {
      var resolved = false;
      var firstSuccess = function(value) {
        if (resolved) { return; }
        resolved = true;
        r.resolve(value);
      };
      var firstFailure = function(reason) {
        if (resolved) { return; }
        resolved = true;
        r.reject(reason);
      };
      futures.forEach(function(f, idx) {
        f.then(firstSuccess, firstFailure);
      });
    }
  });
};

Promise.every = function(/*...futuresOrValues*/) {
  var futures = toPromiseList(arguments);
  return new Promise(function(r) {
    if (!futures.length) {
      r.reject("No futures passed to Promise.every()");
    } else {
      var values = new Array(futures.length);
      var count = 0;
      var accumulate = function(idx, v) {
        count++;
        values[idx] = v;
        if (count == futures.length) {
          r.resolve(values);
        }
      };
      futures.forEach(function(f, idx) {
        f.then(accumulate.bind(null, idx), r.reject);
      });
    }
  });
};

Promise.some = function() {
  var futures = toPromiseList(arguments);
  return new Promise(function(r) {
    if (!futures.length) {
      r.reject("No futures passed to Promise.some()");
    } else {
      var count = 0;
      var accumulateFailures = function(e) {
        count++;
        if (count == futures.length) {
          r.reject();
        }
      };
      futures.forEach(function(f, idx) {
        f.then(r.resolve, accumulateFailures);
      });
    }
  });
};

Promise.fulfill = function(value) {
  return new Promise(function(r) {
    r.fulfill(value);
  });
};

Promise.resolve = function(value) {
  return new Promise(function(r) {
    r.resolve(value);
  });
};

Promise.reject = function(reason) {
  return new Promise(function(r) {
    r.reject(reason);
  });
};

//
// Export
//

global.Promise = Promise;

})(this,
  (typeof window !== 'undefined') ? window : {},
  this.runningUnderTest||false);
<commit_msg>Add console.log(exception) to Promise.js<commit_after>// Copyright (C) 2013:
//    Alex Russell <slightlyoff@chromium.org>
//    Yehuda Katz
//
// Use of this source code is governed by
//    http://www.apache.org/licenses/LICENSE-2.0

// FIXME(slightlyoff):
//    - Document "npm test"
//    - Change global name from "Promise" to something less conflicty
(function(global, browserGlobal, underTest) {
"use strict";

// FIXME(slighltyoff):
//  * aggregates + tests
//  * check on fast-forwarding

underTest = !!underTest;

//
// Async Utilities
//

// Borrowed from RSVP.js
var async;

var MutationObserver = browserGlobal.MutationObserver ||
                       browserGlobal.WebKitMutationObserver;
var Promise;

if (typeof process !== 'undefined' &&
  {}.toString.call(process) === '[object process]') {
  async = function(callback, binding) {
    process.nextTick(function() {
      callback.call(binding);
    });
  };
} else if (MutationObserver) {
  var queue = [];

  var observer = new MutationObserver(function() {
    var toProcess = queue.slice();
    queue = [];
    toProcess.forEach(function(tuple) {
      tuple[0].call(tuple[1]);
    });
  });

  var element = document.createElement('div');
  observer.observe(element, { attributes: true });

  // Chrome Memory Leak: https://bugs.webkit.org/show_bug.cgi?id=93661
  window.addEventListener('unload', function(){
    observer.disconnect();
    observer = null;
  });

  async = function(callback, binding) {
    queue.push([callback, binding]);
    element.setAttribute('drainQueue', 'drainQueue');
  };
} else {
  async = function(callback, binding) {
    setTimeout(function() {
      callback.call(binding);
    }, 1);
  };
}

//
// Object Model Utilities
//

// defineProperties utilities
var _readOnlyProperty = function(v) {
    return {
      enumerable: true,
      configurable: false,
      get: v
    };
};

var _method = function(v, e, c, w) {
    return {
      enumerable:   !!(e || 0),
      configurable: !!(c || 1),
      writable:     !!(w || 1),
      value:           v || function() {}
    };
};

var _pseudoPrivate = function(v) { return _method(v, 0, 1, 0); };
var _public = function(v) { return _method(v, 1); };

//
// Promises Utilities
//

var isThenable = function(any) {
  if (any === undefined)
    return false;
  try {
    var f = any.then;
    if (typeof f == "function") {
      return true;
    }
  } catch (e) { /*squelch*/ }
  return false;
};

var AlreadyResolved = function(name) {
  Error.call(this, name);
};
AlreadyResolved.prototype = Object.create(Error.prototype);

var Backlog = function() {
  var bl = [];
  bl.pump = function(value) {
    async(function() {
      var l = bl.length;
      var x = 0;
      while(x < l) {
        x++;
        bl.shift()(value);
      }
    });
  };
  return bl;
};

//
// Resolver Constuctor
//

var Resolver = function(future,
                        fulfillCallbacks,
                        rejectCallbacks,
                        setValue,
                        setError,
                        setState) {
  var isResolved = false;

  var resolver = this;
  var fulfill = function(value) {
    // console.log("queueing fulfill with:", value);
    async(function() {
      setState("fulfilled");
      setValue(value);
      // console.log("fulfilling with:", value);
      fulfillCallbacks.pump(value);
    });
  };
  var reject = function(reason) {
    // console.log("queuing reject with:", reason);
    async(function() {
      setState("rejected");
      setError(reason);
      // console.log("rejecting with:", reason);
      rejectCallbacks.pump(reason);
    });
  };
  var resolve = function(value) {
    if (isThenable(value)) {
      value.then(resolve, reject);
      return;
    }
    fulfill(value);
  };
  var ifNotResolved = function(func, name) {
    return function(value) {
      if (!isResolved) {
        isResolved = true;
        func(value);
      } else {
        if (typeof console != "undefined") {
          console.error("Cannot resolve a Promise multiple times.");
        }
      }
    };
  };

  // Indirectly resolves the Promise, chaining any passed Promise's resolution
  this.resolve = ifNotResolved(resolve, "resolve");

  // Directly fulfills the future, no matter what value's type is
  this.fulfill = ifNotResolved(fulfill, "fulfill");

  // Rejects the future
  this.reject = ifNotResolved(reject, "reject");

  this.cancel  = function() { resolver.reject(new Error("Cancel")); };
  this.timeout = function() { resolver.reject(new Error("Timeout")); };

  if (underTest) {
    Object.defineProperties(this, {
      _isResolved: _readOnlyProperty(function() { return isResolved; }),
    });
  }

  setState("pending");
};

//
// Promise Constuctor
//

var Promise = function(init) {
  var fulfillCallbacks = new Backlog();
  var rejectCallbacks = new Backlog();
  var value;
  var error;
  var state = "pending";

  if (underTest) {
    Object.defineProperties(this, {
      _value: _readOnlyProperty(function() { return value; }),
      _error: _readOnlyProperty(function() { return error; }),
      _state: _readOnlyProperty(function() { return state; }),
    });
  }

  Object.defineProperties(this, {
    _addAcceptCallback: _pseudoPrivate(
      function(cb) {
        // console.log("adding fulfill callback:", cb);
        fulfillCallbacks.push(cb);
        if (state == "fulfilled") {
          fulfillCallbacks.pump(value);
        }
      }
    ),
    _addRejectCallback: _pseudoPrivate(
      function(cb) {
        // console.log("adding reject callback:", cb);
        rejectCallbacks.push(cb);
        if (state == "rejected") {
          rejectCallbacks.pump(error);
        }
      }
    ),
  });
  var r = new Resolver(this,
                       fulfillCallbacks, rejectCallbacks,
                       function(v) { value = v; },
                       function(e) { error = e; },
                       function(s) { state = s; })
  try {
    if (init) { init(r); }
  } catch(e) {
    r.reject(e);
    console.log(e);
  }
};

//
// Consructor
//

var isCallback = function(any) {
  return (typeof any == "function");
};

// Used in .then()
var wrap = function(callback, resolver, disposition) {
  if (!isCallback(callback)) {
    // If we don't get a callback, we want to forward whatever resolution we get
    return resolver[disposition].bind(resolver);
  }

  return function() {
    try {
      var r = callback.apply(null, arguments);
      resolver.resolve(r);
    } catch(e) {
      // Exceptions reject the resolver
      resolver.reject(e);
      console.log(e);
    }
  };
};

var addCallbacks = function(onfulfill, onreject, scope) {
  if (isCallback(onfulfill)) {
    scope._addAcceptCallback(onfulfill);
  }
  if (isCallback(onreject)) {
    scope._addRejectCallback(onreject);
  }
  return scope;
};

//
// Prototype properties
//

Promise.prototype = Object.create(null, {
  "then": _public(function(onfulfill, onreject) {
    // The logic here is:
    //    We return a new Promise whose resolution merges with the return from
    //    onfulfill() or onerror(). If onfulfill() returns a Promise, we forward
    //    the resolution of that future to the resolution of the returned
    //    Promise.
    var f = this;
    return new Promise(function(r) {
      addCallbacks(wrap(onfulfill, r, "resolve"),
                   wrap(onreject, r, "reject"), f);
    });
  }),
  "catch": _public(function(onreject) {
    var f = this;
    return new Promise(function(r) {
      addCallbacks(null, wrap(onreject, r, "reject"), f);
    });
  }),
});

//
// Statics
//

Promise.isThenable = isThenable;

var toPromiseList = function(list) {
  return Array.prototype.slice.call(list).map(Promise.resolve);
};

Promise.any = function(/*...futuresOrValues*/) {
  var futures = toPromiseList(arguments);
  return new Promise(function(r) {
    if (!futures.length) {
      r.reject("No futures passed to Promise.any()");
    } else {
      var resolved = false;
      var firstSuccess = function(value) {
        if (resolved) { return; }
        resolved = true;
        r.resolve(value);
      };
      var firstFailure = function(reason) {
        if (resolved) { return; }
        resolved = true;
        r.reject(reason);
      };
      futures.forEach(function(f, idx) {
        f.then(firstSuccess, firstFailure);
      });
    }
  });
};

Promise.every = function(/*...futuresOrValues*/) {
  var futures = toPromiseList(arguments);
  return new Promise(function(r) {
    if (!futures.length) {
      r.reject("No futures passed to Promise.every()");
    } else {
      var values = new Array(futures.length);
      var count = 0;
      var accumulate = function(idx, v) {
        count++;
        values[idx] = v;
        if (count == futures.length) {
          r.resolve(values);
        }
      };
      futures.forEach(function(f, idx) {
        f.then(accumulate.bind(null, idx), r.reject);
      });
    }
  });
};

Promise.some = function() {
  var futures = toPromiseList(arguments);
  return new Promise(function(r) {
    if (!futures.length) {
      r.reject("No futures passed to Promise.some()");
    } else {
      var count = 0;
      var accumulateFailures = function(e) {
        count++;
        if (count == futures.length) {
          r.reject();
        }
      };
      futures.forEach(function(f, idx) {
        f.then(r.resolve, accumulateFailures);
      });
    }
  });
};

Promise.fulfill = function(value) {
  return new Promise(function(r) {
    r.fulfill(value);
  });
};

Promise.resolve = function(value) {
  return new Promise(function(r) {
    r.resolve(value);
  });
};

Promise.reject = function(reason) {
  return new Promise(function(r) {
    r.reject(reason);
  });
};

//
// Export
//

global.Promise = Promise;

})(this,
  (typeof window !== 'undefined') ? window : {},
  this.runningUnderTest||false);
<|endoftext|>


<commit_before>const CONTROL_GROUP = '
 \

 \
		 \
		 \
		<input type="text" class="input-nano abbreviation" placeholder="" maxlength="2"> \

 \
			 \

 \

 \

';

const NO_TILES_ADDED_TEXT = "No tiles added.";

const SUBMIT_BUTTON_SAVING_TEXT = "Saving…";
const SUBMIT_BUTTON_SUBMIT_TEXT = "Save";

const FAVICON_LOAD_FAIL_TITLE = "Failed to retrieve favicon!";
const FAVICON_LOAD_FAIL_URL_REPLACE = "#{url}";
const FAVICON_LOAD_FAIL_MESSAGE = "Could not retrieve favicon for #{url}. Check the URL and ensure the site does not redirect.";

$(document).ready(function() {
	var sites = [];

	sitesReload();

	chrome.extension.onMessage.addListener(function(request, sender, sendResponse) {
		console.log('Message received: ' + request.message);

		if (request.message == "saved" || request.message == "deleted") {
			sitesReload();
		}
	});

	function sitesReload() {
		getSites(function (data) {
			$("#sites").html("");

			if (!data) {
				$("#sites").prepend(NO_TILES_ADDED_TEXT);
			} else {
				sites = data;

				sites = sites.reverse();

				for (var i = 0; i < sites.length; i++) {
					var newControlGroup = CONTROL_GROUP;
					var site = sites[i];

					newControlGroup = $(newControlGroup);
					newControlGroup.find('span.url').html(site.url);
					newControlGroup.find('input.abbreviation').val(site.abbreviation);
					newControlGroup.find('a.remove').on('click', function(e) {
						removeControlGroup(e.target);
					});

					$("#sites").prepend(newControlGroup);
				}
			}

			$("#sites").sortable({
				handle: '.handle',
				axis: 'y',
				update: function(e, ui) {
					movedControlGroup(ui);
				}
			});

			$(".container").removeClass("hidden");
		});
	}

	function movedControlGroup(ui) {
		makeSites();

		chrome.extension.sendMessage({ message: "saved" }, function(response) { });
	}

	function removeControlGroup(element) {
		parent = $(element).parents('.control-group');

		chrome.extension.sendMessage({ message: "delete", url:parent.find('span.url').html() }, function(response) { });
	}

	function makeSites() {
		var fields = [];

		$('#sites .site-controls').each(function(index, element) {
			urlField = $(this).children('span.url').eq(0);
			abbreviationField = $(this).children('input:text.abbreviation').eq(0);

			var url = urlField.html();
			var abbreviation = abbreviationField.val();

			if (!url.match(/^(http|https):\/\//)) {
				url = "http://" + url;

				urlField.html(url);
			}

			if (!abbreviation) {
				abbreviation = makeAbbreviation(getHostname(url));

				abbreviationField.val(abbreviation);
			}

			fields[index] = {};
			fields[index].url = url;
			fields[index].abbreviation = abbreviation;
		});

		var numberOfSitesRequiringColor = fields.length;

		const TIME_BEFORE_UPDATE = 1000 * 60 * 60;

		getSites(function(sites) {
			for (var i = 0; i < fields.length; i++) {
				fields[i].color = null;
			}

			if (sites != null && sites.length != 0) {
				for (var i = 0; i < sites.length; i++) {
					for (var j = 0; j < fields.length; j++) {
						if (sites[i].url == fields[j].url) {
							if (siteNeedsColorUpdate(sites[i])) {
								fields[j].color = null;
							} else {
								fields[j].color = sites[i].color;
								fields[j].lastUpdated = sites[i].lastUpdated;

								numberOfSitesRequiringColor--;
							}
						}
					}
				}
			}

			console.log(Math.min(numberOfSitesRequiringColor, 0) + " sites require a color check");

			var siteSaved = false;

			for (var i = 0; i < fields.length; i++) {
				(function() {
					var site = fields[i];

					if (site.color != null) {
						if (!siteSaved && numberOfSitesRequiringColor <= 0) {
							saveSites(fields);

							siteSaved = true;
						}
					} else {
						setSiteColor(site, function(site, error) {
							if (error) {
								$(".alert-error").removeClass("hidden");
								$(".alert-error").children("span").html(FAVICON_LOAD_FAIL_MESSAGE.replace(FAVICON_LOAD_FAIL_URL_REPLACE, site.url));
								$(".alert-error").children("h4").html(FAVICON_LOAD_FAIL_TITLE);
							}

							numberOfSitesRequiringColor--;

							console.log((fields.length - numberOfSitesRequiringColor) + " / " + fields.length + " – Got color for " + site.url);

							if (!siteSaved && numberOfSitesRequiringColor <= 0) {
								saveSites(fields);

								siteSaved = true;
							}
						});
					}
				})();
			}

			return sites;
		});
	}
});<commit_msg>Check for empty sites array on options page to display no tiles added message.<commit_after>const CONTROL_GROUP = '
 \

 \
		 \
		 \
		<input type="text" class="input-nano abbreviation" placeholder="" maxlength="2"> \

 \
			 \

 \

 \

';

const NO_TILES_ADDED_TEXT = "No tiles added.";

const SUBMIT_BUTTON_SAVING_TEXT = "Saving…";
const SUBMIT_BUTTON_SUBMIT_TEXT = "Save";

const FAVICON_LOAD_FAIL_TITLE = "Failed to retrieve favicon!";
const FAVICON_LOAD_FAIL_URL_REPLACE = "#{url}";
const FAVICON_LOAD_FAIL_MESSAGE = "Could not retrieve favicon for #{url}. Check the URL and ensure the site does not redirect.";

$(document).ready(function() {
	chrome.storage.sync.remove("sites");

	chrome.storage.sync.get(null, function(items) {
		console.log(items);
	});

	var sites = [];

	sitesReload();

	chrome.extension.onMessage.addListener(function(request, sender, sendResponse) {
		console.log('Message received: ' + request.message);

		if (request.message == "saved" || request.message == "deleted") {
			sitesReload();
		}
	});

	function sitesReload() {
		getSites(function (items) {
			$("#sites").html("");

			if (!items || items.length == 0) {
				$("#sites").prepend(NO_TILES_ADDED_TEXT);
			} else {
				sites = items;

				sites = sites.reverse();

				for (var i = 0; i < sites.length; i++) {
					var newControlGroup = CONTROL_GROUP;
					var site = sites[i];

					newControlGroup = $(newControlGroup);
					newControlGroup.find('span.url').html(site.url);
					newControlGroup.find('input.abbreviation').val(site.abbreviation);
					newControlGroup.find('a.remove').on('click', function(e) {
						removeControlGroup(e.target);
					});

					$("#sites").prepend(newControlGroup);
				}
			}

			$("#sites").sortable({
				handle: '.handle',
				axis: 'y',
				update: function(e, ui) {
					movedControlGroup(ui);
				}
			});

			$(".container").removeClass("hidden");
		});
	}

	function movedControlGroup(ui) {
		makeSites();

		chrome.extension.sendMessage({ message: "saved" }, function(response) { });
	}

	function removeControlGroup(element) {
		parent = $(element).parents('.control-group');

		chrome.extension.sendMessage({ message: "delete", url:parent.find('span.url').html() }, function(response) { });
	}

	function makeSites() {
		var fields = [];

		$('#sites .site-controls').each(function(index, element) {
			urlField = $(this).children('span.url').eq(0);
			abbreviationField = $(this).children('input:text.abbreviation').eq(0);

			var url = urlField.html();
			var abbreviation = abbreviationField.val();

			if (!url.match(/^(http|https):\/\//)) {
				url = "http://" + url;

				urlField.html(url);
			}

			if (!abbreviation) {
				abbreviation = makeAbbreviation(getHostname(url));

				abbreviationField.val(abbreviation);
			}

			fields[index] = {};
			fields[index].url = url;
			fields[index].abbreviation = abbreviation;
		});

		var numberOfSitesRequiringColor = fields.length;

		const TIME_BEFORE_UPDATE = 1000 * 60 * 60;

		getSites(function(sites) {
			for (var i = 0; i < fields.length; i++) {
				fields[i].color = null;
			}

			if (sites != null && sites.length != 0) {
				for (var i = 0; i < sites.length; i++) {
					for (var j = 0; j < fields.length; j++) {
						if (sites[i].url == fields[j].url) {
							if (siteNeedsColorUpdate(sites[i])) {
								fields[j].color = null;
							} else {
								fields[j].color = sites[i].color;
								fields[j].lastUpdated = sites[i].lastUpdated;

								numberOfSitesRequiringColor--;
							}
						}
					}
				}
			}

			console.log(Math.min(numberOfSitesRequiringColor, 0) + " sites require a color check");

			var siteSaved = false;

			for (var i = 0; i < fields.length; i++) {
				(function() {
					var site = fields[i];

					if (site.color != null) {
						if (!siteSaved && numberOfSitesRequiringColor <= 0) {
							saveSites(fields);

							siteSaved = true;
						}
					} else {
						setSiteColor(site, function(site, error) {
							if (error) {
								$(".alert-error").removeClass("hidden");
								$(".alert-error").children("span").html(FAVICON_LOAD_FAIL_MESSAGE.replace(FAVICON_LOAD_FAIL_URL_REPLACE, site.url));
								$(".alert-error").children("h4").html(FAVICON_LOAD_FAIL_TITLE);
							}

							numberOfSitesRequiringColor--;

							console.log((fields.length - numberOfSitesRequiringColor) + " / " + fields.length + " – Got color for " + site.url);

							if (!siteSaved && numberOfSitesRequiringColor <= 0) {
								saveSites(fields);

								siteSaved = true;
							}
						});
					}
				})();
			}

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


<commit_before>
"use strict"

var WAT;
var stylesConfig;
var getCustomCssFile, customCssFileLoadHandler, loadCustomCssFileString, customStylesFromFile, hideElementsFromManifest, scriptString, cssString;

// Public API
var self = {
    init: function (WATref) {
        if (!WAT) {
            WAT = WATref;
            stylesConfig = WAT.manifest.wat_styles || {};

            // Execute element hiding
            WAT.components.webView.addEventListener("MSWebViewDOMContentLoaded", hideElementsFromManifest);

            if (stylesConfig.customCssFile) {
                getCustomCssFile();
            }
        }
    }
};

// Private Methods

getCustomCssFile = function () {
    var cssFile = "ms-appx://" + ((/^\//.test(stylesConfig.customCssFile)) ? "" : "/") + stylesConfig.customCssFile;

    // TODO: log

    var url = new Windows.Foundation.Uri(cssFile);
    Windows.Storage.StorageFile.getFileFromApplicationUriAsync(url)
        .then(
            customCssFileLoadHandler,
            function (err) {
                // TODO: log this error, but let things proceed anyway
            }
        );
};

customCssFileLoadHandler = function (file) {
    Windows.Storage.FileIO.readTextAsync(file)
        .then(
            function (text) {
                customStylesFromFile = text;
                WAT.components.webView.addEventListener("MSWebViewDOMContentLoaded", loadCustomCssFileString);
            },
            function (err) {
                // TODO: log this error, but let things proceed anyway
            }
        );
};

loadCustomCssFileString = function () {
    var exec, scriptString;

    // TODO: log

    scriptString = "var cssFileString = '" + customStylesFromFile.replace(/\r\n/gm, " ") + "';" +
        "var cssFileStyleEl = document.createElement('style');" +
        "document.body.appendChild(cssFileStyleEl);" +
        "cssFileStyleEl.innerHTML = cssFileString;";

    exec = WAT.components.webView.invokeScriptAsync("eval", scriptString);
    exec.start();
};

hideElementsFromManifest = function () {
    var i, l, hiddenEls, elements, exec,
        scriptString = "",
        cssString = "";

    if (stylesConfig.hiddenElements && stylesConfig !== "") {
        hiddenEls = stylesConfig.hiddenElements;
        elements = "";
        for (i = 0; i < hiddenEls.length - 1; i++) {
            elements += hiddenEls[i] + ",";
        }
        elements += hiddenEls[hiddenEls.length - 1];
        cssString += elements + "{display:none !important;}";
    }

    scriptString = "var cssString = '" + cssString + "';" +
            "var styleEl = document.createElement('style');" +
            "document.body.appendChild(styleEl);" +
            "styleEl.innerHTML = cssString;";

    exec = WAT.components.webView.invokeScriptAsync("eval", scriptString);
    exec.start();
}

module.exports = self; // exports
<commit_msg>[windows] logging for style injection module.<commit_after>
"use strict"

var WAT;
var stylesConfig;
var getCustomCssFile, customCssFileLoadHandler, loadCustomCssFileString, customStylesFromFile, hideElementsFromManifest, scriptString, cssString,
logger = window.console;

// Public API
var self = {
    init: function (WATref) {
        if (!WAT) {
            WAT = WATref;
            stylesConfig = WAT.manifest.wat_styles || {};

            // Execute element hiding
            WAT.components.webView.addEventListener("MSWebViewDOMContentLoaded", hideElementsFromManifest);

            if (stylesConfig.customCssFile) {
                getCustomCssFile();
            }
        }
    }
};

// Private Methods

getCustomCssFile = function () {
    var cssFile = "ms-appx://" + ((/^\//.test(stylesConfig.customCssFile)) ? "" : "/") + stylesConfig.customCssFile;

    logger.log("Getting custom css file from " + cssFile);

    var url = new Windows.Foundation.Uri(cssFile);
    Windows.Storage.StorageFile.getFileFromApplicationUriAsync(url)
        .then(
            customCssFileLoadHandler,
            function (err) {
                // log this error, but let things proceed anyway
                logger.error("Error getting custom css file", err);
            }
        );
};

customCssFileLoadHandler = function (file) {
    Windows.Storage.FileIO.readTextAsync(file)
        .then(
            function (text) {
                customStylesFromFile = text;
                WAT.components.webView.addEventListener("MSWebViewDOMContentLoaded", loadCustomCssFileString);
            },
            function (err) {
                // log this error, but let things proceed anyway
                logger.warn("Error reading custom css file", err);
            }
        );
};

loadCustomCssFileString = function () {
    var exec, scriptString;

    logger.log("injecting styles: ", customStylesFromFile.replace(/\r\n/gm, " "));

    scriptString = "var cssFileString = '" + customStylesFromFile.replace(/\r\n/gm, " ") + "';" +
        "var cssFileStyleEl = document.createElement('style');" +
        "document.body.appendChild(cssFileStyleEl);" +
        "cssFileStyleEl.innerHTML = cssFileString;";

    exec = WAT.components.webView.invokeScriptAsync("eval", scriptString);
    exec.start();
};

hideElementsFromManifest = function () {
    var i, l, hiddenEls, elements, exec,
        scriptString = "",
        cssString = "";

    if (stylesConfig.hiddenElements && stylesConfig !== "") {
        hiddenEls = stylesConfig.hiddenElements;
        elements = "";
        for (i = 0; i < hiddenEls.length - 1; i++) {
            elements += hiddenEls[i] + ",";
        }
        elements += hiddenEls[hiddenEls.length - 1];
        cssString += elements + "{display:none !important;}";
    }

    scriptString = "var cssString = '" + cssString + "';" +
            "var styleEl = document.createElement('style');" +
            "document.body.appendChild(styleEl);" +
            "styleEl.innerHTML = cssString;";

    exec = WAT.components.webView.invokeScriptAsync("eval", scriptString);
    exec.start();
}

module.exports = self; // exports
<|endoftext|>


<commit_before>'use strict';

(function (angular) {
    angular.module('youtubePluginWidget')
        .controller('WidgetHomeCtrl', ['DataStore', 'TAG_NAMES', 'STATUS_CODE', function (DataStore, TAG_NAMES, STATUS_CODE) {
            var WidgetHome = this;
            WidgetHome.data = null;
            /*
             * Fetch users's data from datastore
             */
            var init = function () {
                var success = function (result) {
                        WidgetHome.data = result.data;
                        console.info('WidgetHomeCtrl success result:----------:::::::::', WidgetHome.data );
                    }
                    , error = function (err) {
                        if (err && err.code !== STATUS_CODE.NOT_FOUND) {
                            console.error('Error while getting data', err);
                        }
                    };
                DataStore.get(TAG_NAMES.YOUTUBE_INFO).then(success, error);
            };
            init();
        }])
})(window.angular);
<commit_msg>Added onupdate method in service for widget section<commit_after>'use strict';

(function (angular) {
    angular.module('youtubePluginWidget')
        .controller('WidgetHomeCtrl', ['DataStore', 'TAG_NAMES', 'STATUS_CODE', function (DataStore, TAG_NAMES, STATUS_CODE) {
            var WidgetHome = this;
            WidgetHome.data = null;
            /*
             * Fetch user's data from datastore
             */
            var init = function () {
                var success = function (result) {
                        WidgetHome.data = result.data;
                    }
                    , error = function (err) {
                        if (err && err.code !== STATUS_CODE.NOT_FOUND) {
                            console.error('Error while getting data', err);
                        }
                    };
                DataStore.get(TAG_NAMES.YOUTUBE_INFO).then(success, error);
            };
            init();

            var onUpdateCallback = function (event) {
                if (event && event.tag === TAG_NAMES.YOUTUBE_INFO) {
                    WidgetHome.data = event.obj;
                }
            };
            DataStore.onUpdate().then(function(){},function(){},onUpdateCallback);
        }])
})(window.angular);
<|endoftext|>


<commit_before>
function appendLog(text, color) {
  var dt = new Date();
  var hour = dt.getHours() < 10 ? "0" + String(dt.getHours()) : String(dt.getHours());
  var minute  = dt.getHours() < 10 ? "0" + String(dt.getMinutes()) : String(dt.getMinutes());
  var seconds = dt.getSeconds() < 10 ? "0" + String(dt.getSeconds()) : String(dt.getSeconds());

  var log_text = "[" + hour + ":" + minute + ":" + seconds + "] " + text;
  var $row = $("

").text(log_text);
  if(color) $row.css("color", color);

  $("#log").append($row);

  setTimeout(function() {
      var h = $("#log")[0].scrollHeight;
      $("#log").animate({ scrollTop: h }, "fast");
  }, 5);
}

function appendHtmlLog(html) {
    $("#log").append($(html));

    setTimeout(function() {
        var h = $("#log")[0].scrollHeight;
        $("#log").animate({ scrollTop: h }, "fast");
    }, 5);
}

function ws_close_handler(name) {
    return function(v) {
        if(v.code == 1000) {
            if(v.reason) {
                appendLog(name + "WS CLOSED NORMAL; reason='" + v.reason + "'");
            } else {
                appendLog(name + "WS CLOSED NORMAL");
            }
        } else if(v.code == 1006) {
            appendLog(name + "WS CLOSED ABNORMAL", "rgb(208, 142, 40)");
        } else {
            appendLog(name + "WS CLOSED, code=" + v.code + "; reason=" + v.reason, "#c00");
        }
    }
}

function DiscoverWS(addCallback, removeCallback) {
    var self = this;

    this.ws = new WebSocket("ws://" + window.location.host + "/ws/discover");
    this.ws.onopen = function() {
      appendLog("Discover WS Connected", "#0000aa");
    };
    this.ws.onmessage = function(m) {
      var payload = JSON.parse(m.data);

      if(payload.alive) {
          if(addCallback) {
              addCallback(payload.uuid, payload.serial, payload.name,
                     payload.version, payload.password, payload.ipaddr);
          }
      } else {
          if(removeCallback) removeCallback(payload.serial);
      }
    }

    this.ws.onclose = ws_close_handler("Discover");

    this.close = function() {
        self.ws.close();
        self.ws = undefined;
    }
}

function addDevice(uuid, serial, name, version, password, ipaddr) {
  var $item = $("").
    attr("href", "#" + serial + ";" + name).
    addClass("list-group-item").
    attr("data-serial", serial).
    attr("data-password", password ? "true" : "false").
    attr("data-name", name);

  var $row1 = $("

");
  var $row2 = $("

");
  $item.append($row1);
  $item.append($row2);

  if(password) $row1.append($("").addClass("glyphicon glyphicon-lock"));
  $row1.append($("").text(serial).addClass("label label-primary"));
  $row1.append($("").text(" "));
  $row1.append($("").text(name));

  $row2.append($("").text(uuid).addClass("label label-default"));
  $row2.append($("").text(" "));
  $row2.append($("").text(version));
  $row2.append($("").text(" / "));
  $row2.append($("").text(ipaddr));

  $("#devices").append($item);
}

function removeDevice(serial) {
  $("[data-device=" + serial + "]").remove();
}

function startDiscover() {
  $("#devices").children().remove();
  if(window.discover_ws) stopDiscover();
  window.discover_ws = new DiscoverWS(addDevice, removeDevice);
  $("[data-role=discover]").removeClass("btn-success").addClass("btn-warning").text("Stop Discover");
}

function stopDiscover() {
  if(!window.discover_ws) return;
  window.discover_ws.close();
  window.discover_ws = undefined;
  $("[data-role=discover]").removeClass("btn-warning").addClass("btn-success").text("Start Discover");
}

<commit_msg>Bugfix<commit_after>
function appendLog(text, color) {
  var dt = new Date();
  var hour = dt.getHours() < 10 ? "0" + String(dt.getHours()) : String(dt.getHours());
  var minute  = dt.getHours() < 10 ? "0" + String(dt.getMinutes()) : String(dt.getMinutes());
  var seconds = dt.getSeconds() < 10 ? "0" + String(dt.getSeconds()) : String(dt.getSeconds());

  var log_text = "[" + hour + ":" + minute + ":" + seconds + "] " + text;
  var $row = $("

").text(log_text);
  if(color) $row.css("color", color);

  $("#log").append($row);

  setTimeout(function() {
      var h = $("#log")[0].scrollHeight;
      $("#log").animate({ scrollTop: h }, "fast");
  }, 5);
}

function appendHtmlLog(html) {
    $("#log").append($(html));

    setTimeout(function() {
        var h = $("#log")[0].scrollHeight;
        $("#log").animate({ scrollTop: h }, "fast");
    }, 5);
}

function ws_close_handler(name) {
    return function(v) {
        if(v.code == 1000) {
            if(v.reason) {
                appendLog(name + "WS CLOSED NORMAL; reason='" + v.reason + "'");
            } else {
                appendLog(name + "WS CLOSED NORMAL");
            }
        } else if(v.code == 1006) {
            appendLog(name + "WS CLOSED ABNORMAL", "rgb(208, 142, 40)");
        } else {
            appendLog(name + "WS CLOSED, code=" + v.code + "; reason=" + v.reason, "#c00");
        }
    }
}

function DiscoverWS(addCallback, removeCallback) {
    var self = this;

    this.ws = new WebSocket("ws://" + window.location.host + "/ws/discover");
    this.ws.onopen = function() {
      appendLog("Discover WS Connected", "#0000aa");
    };
    this.ws.onmessage = function(m) {
      var payload = JSON.parse(m.data);

      if(payload.alive) {
          if(addCallback) {
              addCallback(payload.uuid, payload.serial, payload.name,
                     payload.version, payload.password, payload.ipaddr);
          }
      } else {
          if(removeCallback) removeCallback(payload.serial);
      }
    }

    this.ws.onclose = ws_close_handler("Discover");

    this.close = function() {
        self.ws.close();
        self.ws = undefined;
    }
}

function addDevice(uuid, serial, name, version, password, ipaddr) {
  var $item = $("").
    attr("href", "#" + serial + ";" + name).
    addClass("list-group-item").
    attr("data-serial", uuid).
    attr("data-uuid", uuid).
    attr("data-password", password ? "true" : "false").
    attr("data-name", name);

  var $row1 = $("

");
  var $row2 = $("

");
  $item.append($row1);
  $item.append($row2);

  if(password) $row1.append($("").addClass("glyphicon glyphicon-lock"));
  $row1.append($("").text(serial).addClass("label label-primary"));
  $row1.append($("").text(" "));
  $row1.append($("").text(name));

  $row2.append($("").text(uuid).addClass("label label-default"));
  $row2.append($("").text(" "));
  $row2.append($("").text(version));
  $row2.append($("").text(" / "));
  $row2.append($("").text(ipaddr));

  $("#devices").append($item);
}

function removeDevice(serial) {
  $("[data-device=" + serial + "]").remove();
}

function startDiscover() {
  $("#devices").children().remove();
  if(window.discover_ws) stopDiscover();
  window.discover_ws = new DiscoverWS(addDevice, removeDevice);
  $("[data-role=discover]").removeClass("btn-success").addClass("btn-warning").text("Stop Discover");
}

function stopDiscover() {
  if(!window.discover_ws) return;
  window.discover_ws.close();
  window.discover_ws = undefined;
  $("[data-role=discover]").removeClass("btn-warning").addClass("btn-success").text("Start Discover");
}

<|endoftext|>


<commit_before>/**
 * Shuggest - Display file suggestions of the Jenkins userContent directory
 *            next to shell command textareas.
 *
 * This is intended to be used as userscript or "Page Markup"
 * (see the identically named Jenkins plugin) script.
 *
 * Requires
 * - jQuery 1.4.2 or higher
 * - shuggest.css
 * - index.json in userContent/ in the following format
 *   (Can be generated with index-usercontent.sh)
 * 	 ```[{path: "path/to/script.sh", description: "Short description"}, ...]```
 *
 *
 * @author Thomas Lehmann, May 2012
 * @licence MIT
 *
 */
(function() {

	var SHELL_SCRIPT_IDX = "/userContent/index.json/*view*/",
		DATA_ATTR_SH_CMD = "data-us-sh-cmd",
		DATA_ATTR_TOOLTIP_ATTACHED = "data-us-attached";

	var shuggestData = null;

	function escapeHtml(str){
		return str.replace(/"/g, """).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
	}

	function renderDetail(str, cls){
		if (str) {
			return '
'
				+ escapeHtml(str) + '
';
		}
		return "";
	}

	function renderItem(item) {
		var path = item.path;

		var str =  '
'
			+ '
'
				+ '' + path + ''
				+ ''+ escapeHtml(item.description) +''
				+ 'more ...'
			+ '
'
			+ '
'
				+ '
'
					+ escapeHtml(item.description)
					+' [show]'
				+ '
'
				+ renderDetail(item.usage, "usage")
				+ renderDetail(item.consumes, "consumes")
				+ renderDetail(item.produces, "produces")
			+ '
'
		+ '
';

		return str;
	}

	function renderList(list) {
		var s = "
", i;
		for (i = 0; i < list.length; i++) {
			s += renderItem(list[i]);
		}
		return s + "
";
	}

	function prepareShellCommand(command){
		return "$JENKINS_HOME/userContent/" + command;
	}

	function applyTooltips(){
		// iterating all (shell command) input boxes
		jQuery('textarea[name="command"]')
			.each(function(idx, el) {
				el = jQuery(el);

				/* Testing for previously attached tooltip.
				 * This function is also called by a click handler catching
				 * the new shell command item click.
				 */
				if (el.attr(DATA_ATTR_TOOLTIP_ATTACHED) == "true") {
					if (typeof console !== "undefined") {
						console.debug("Shuggest: tooltip already attached to input element.");
					}
					return;
				}

				decorate(el);

				el.attr(DATA_ATTR_TOOLTIP_ATTACHED, true);
		});
	}

	function decorate(el) {
		var aEl, moreEl, infoEl = jQuery('
'
			+ renderList(shuggestData)
			+ 'x
');

		if (typeof console !== "undefined") {
			console.debug("Shuggest: decorating textarea el", el, renderList(shuggestData));
		}

		el.parent().append(infoEl);
		el.bind("focus", function() {
			infoEl.show();
		});

		aEl = infoEl.find("a[" + DATA_ATTR_SH_CMD + "]");
		moreEl = infoEl.find("a.shuggest-more");

		aEl.click(function() {
			var linkEl = jQuery(this), textEl, shellCommand;

			// get textarea next to tooltip's element
			textEl = linkEl
				.parents("td.setting-main")
				.find("textarea");

			shellCommand = prepareShellCommand(
				linkEl.attr(DATA_ATTR_SH_CMD)
			);

			if (typeof console !== "undefined") {
				console.debug("Shuggest: clicked command link", linkEl, textEl, shellCommand);
			}

			textEl[0].value = textEl[0].value + "\n" + shellCommand;
		});

		moreEl.click(function(){
			var linkEl = jQuery(this),
				detailEl = linkEl.parent().next(),
				allDetailEl = linkEl.parents("ul").find(".shuggest-details"),
				isOpen = detailEl.hasClass("shuggest-details-open");

			allDetailEl.removeClass("shuggest-details-open");

			if (isOpen) {
				return;
			}

			detailEl.addClass("shuggest-details-open");
		});

		infoEl.children(".shuggest-tooltip-close")
			.click(function() {
				infoEl.hide();
			});
	}

	// load shell script index
	jQuery.ajax({
		url : SHELL_SCRIPT_IDX,
		success : function(data) {
			shuggestData = eval("(" + data + ")");

			if (typeof console !== "undefined") {
				console.debug("Shuggest: loaded Shuggest data", shuggestData);
			}

			applyTooltips();
		}
	});

	// monitor click on "new command" items
	jQuery("body").click(function(e){
		var item;

		if (e.target.nodeName !== "A") {
	        return;
	    }

	    item = jQuery(e.originalTarget);

	    if (item.hasClass("yuimenuitemlabel-selected")) {
	    	if (typeof console !== "undefined") {
	    		console.debug("Shuggest: clicked item in Add XYZ-Step Menu");
	    	}

	        window.setTimeout(function(){
	        	applyTooltips();
	        },1000);
	    }
	});

})();
<commit_msg>Fixed index.json loading: determining base URL and prefix AJAX with it<commit_after>/**
 * Shuggest - Display file suggestions of the Jenkins userContent directory
 *            next to shell command textareas.
 *
 * This is intended to be used as userscript or "Page Markup"
 * (see the identically named Jenkins plugin) script.
 *
 * Requires
 * - jQuery 1.4.2 or higher
 * - shuggest.css
 * - index.json in userContent/ in the following format
 *   (Can be generated with index-usercontent.sh)
 * 	 ```[{path: "path/to/script.sh", description: "Short description"}, ...]```
 *
 *
 * @author Thomas Lehmann, May 2012
 * @licence MIT
 *
 */
(function() {

	var SHELL_SCRIPT_IDX = "/userContent/index.json/*view*/",
		DATA_ATTR_SH_CMD = "data-us-sh-cmd",
		DATA_ATTR_TOOLTIP_ATTACHED = "data-us-attached";

	var basePath = jQuery('script[src$="shuggest.js"]').attr("src").replace(/userContent\/.*$/, ""),
		shuggestData = null;

	function escapeHtml(str){
		return str.replace(/"/g, """).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
	}

	function renderDetail(str, cls){
		if (str) {
			return '
'
				+ escapeHtml(str) + '
';
		}
		return "";
	}

	function renderItem(item) {
		var path = item.path;

		var str =  '
'
			+ '
'
				+ '' + path + ''
				+ ''+ escapeHtml(item.description) +''
				+ 'more ...'
			+ '
'
			+ '
'
				+ '
'
					+ escapeHtml(item.description)
					+' [show]'
				+ '
'
				+ renderDetail(item.usage, "usage")
				+ renderDetail(item.consumes, "consumes")
				+ renderDetail(item.produces, "produces")
			+ '
'
		+ '
';

		return str;
	}

	function renderList(list) {
		var s = "
", i;
		for (i = 0; i < list.length; i++) {
			s += renderItem(list[i]);
		}
		return s + "
";
	}

	function prepareShellCommand(command){
		return "$JENKINS_HOME/userContent/" + command;
	}

	function applyTooltips(){
		// iterating all (shell command) input boxes
		jQuery('textarea[name="command"]')
			.each(function(idx, el) {
				el = jQuery(el);

				/* Testing for previously attached tooltip.
				 * This function is also called by a click handler catching
				 * the new shell command item click.
				 */
				if (el.attr(DATA_ATTR_TOOLTIP_ATTACHED) == "true") {
					if (typeof console !== "undefined") {
						console.debug("Shuggest: tooltip already attached to input element.");
					}
					return;
				}

				decorate(el);

				el.attr(DATA_ATTR_TOOLTIP_ATTACHED, true);
		});
	}

	function decorate(el) {
		var aEl, moreEl, infoEl = jQuery('
'
			+ renderList(shuggestData)
			+ 'x
');

		if (typeof console !== "undefined") {
			console.debug("Shuggest: decorating textarea el", el, renderList(shuggestData));
		}

		el.parent().append(infoEl);
		el.bind("focus", function() {
			infoEl.show();
		});

		aEl = infoEl.find("a[" + DATA_ATTR_SH_CMD + "]");
		moreEl = infoEl.find("a.shuggest-more");

		aEl.click(function() {
			var linkEl = jQuery(this), textEl, shellCommand;

			// get textarea next to tooltip's element
			textEl = linkEl
				.parents("td.setting-main")
				.find("textarea");

			shellCommand = prepareShellCommand(
				linkEl.attr(DATA_ATTR_SH_CMD)
			);

			if (typeof console !== "undefined") {
				console.debug("Shuggest: clicked command link", linkEl, textEl, shellCommand);
			}

			textEl[0].value = textEl[0].value + "\n" + shellCommand;
		});

		moreEl.click(function(){
			var linkEl = jQuery(this),
				detailEl = linkEl.parent().next(),
				allDetailEl = linkEl.parents("ul").find(".shuggest-details"),
				isOpen = detailEl.hasClass("shuggest-details-open");

			allDetailEl.removeClass("shuggest-details-open");

			if (isOpen) {
				return;
			}

			detailEl.addClass("shuggest-details-open");
		});

		infoEl.children(".shuggest-tooltip-close")
			.click(function() {
				infoEl.hide();
			});
	}

	// load shell script index
	jQuery.ajax({
		url: basePath + "/" + SHELL_SCRIPT_IDX,
		success : function(data) {
			shuggestData = eval("(" + data + ")");

			if (typeof console !== "undefined") {
				console.debug("Shuggest: loaded Shuggest data", shuggestData);
			}

			applyTooltips();
		}
	});

	// monitor click on "new command" items
	jQuery("body").click(function(e){
		var item;

		if (e.target.nodeName !== "A") {
	        return;
	    }

	    item = jQuery(e.originalTarget);

	    if (item.hasClass("yuimenuitemlabel-selected")) {
	    	if (typeof console !== "undefined") {
	    		console.debug("Shuggest: clicked item in Add XYZ-Step Menu");
	    	}

	        window.setTimeout(function(){
	        	applyTooltips();
	        },1000);
	    }
	});

})();
<|endoftext|>


<commit_before>dojo.provide("dijit.Dialog");

dojo.require("dojo.dnd.move");
dojo.require("dojo.dnd.TimedMoveable");
dojo.require("dojo.fx");

dojo.require("dijit._Widget");
dojo.require("dijit._Templated");
dojo.require("dijit.form._FormMixin");
dojo.require("dijit._DialogMixin");
dojo.require("dijit.DialogUnderlay");
dojo.require("dijit.layout.ContentPane");
dojo.requireLocalization("dijit", "common");

/*=====
dijit._underlay = function(kwArgs){
	// summary:
	//		A shared instance of a `dijit.DialogUnderlay`
	//
	// description:
	//		A shared instance of a `dijit.DialogUnderlay` created and
	//		used by `dijit.Dialog`, though never created until some Dialog
	//		or subclass thereof is shown.
};
=====*/

dojo.declare(
	"dijit.Dialog",
	[dijit.layout.ContentPane, dijit._Templated, dijit.form._FormMixin, dijit._DialogMixin],
	{
		// summary:
		//		A modal dialog Widget
		//
		// description:
		//		Pops up a modal dialog window, blocking access to the screen
		//		and also graying out the screen Dialog is extended from
		//		ContentPane so it supports all the same parameters (href, etc.)
		//
		// example:
		// |

		//
		// example:
		// |	var foo = new dijit.Dialog({ title: "test dialog", content: "test content" };
		// |	dojo.body().appendChild(foo.domNode);
		// |	foo.startup();

		templateString: null,
		templatePath: dojo.moduleUrl("dijit", "templates/Dialog.html"),
		attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, {
			title: [
				{ node: "titleNode", type: "innerHTML" },
				{ node: "titleBar", type: "attribute" }
			]
		}),

		// open: Boolean
		//		True if Dialog is currently displayed on screen.
		open: false,

		// duration: Integer
		//		The time in milliseconds it takes the dialog to fade in and out
		duration: dijit.defaultDuration,

		// refocus: Boolean
		// 		A Toggle to modify the default focus behavior of a Dialog, which
		// 		is to re-focus the element which had focus before being opened.
		//		False will disable refocusing. Default: true
		refocus: true,

		// autofocus: Boolean
		// 		A Toggle to modify the default focus behavior of a Dialog, which
		// 		is to focus on the first dialog element after opening the dialog.
		//		False will disable autofocusing. Default: true
		autofocus: true,

		// _firstFocusItem: [private] [readonly] DomNode
		//		The pointer to the first focusable node in the dialog.
		//		Set by `dijit._DialogMixin._getFocusItems`.
		_firstFocusItem: null,

		// _lastFocusItem: [private] [readonly] DomNode
		//		The pointer to which node has focus prior to our dialog.
		//		Set by `dijit._DialogMixin._getFocusItems`.
		_lastFocusItem: null,

		// doLayout: [protected] Boolean
		//		Don't change this parameter from the default value.
		//		This ContentPane parameter doesn't make sense for Dialog, since Dialog
		//		is never a child of a layout container, nor can you specify the size of
		//		Dialog in order to control the size of an inner widget.
		doLayout: false,

		// draggable: Boolean
		//		Toggles the moveable aspect of the Dialog. If true, Dialog
		//		can be dragged by it's title. If false it will remain centered
		//		in the viewport.
		draggable: true,

		postMixInProperties: function(){
			var _nlsResources = dojo.i18n.getLocalization("dijit", "common");
			dojo.mixin(this, _nlsResources);
			this.inherited(arguments);
		},

		postCreate: function(){
			dojo.style(this.domNode, {
				visibility:"hidden",
				position:"absolute",
				display:"",
				top:"-9999px"
			});
			dojo.body().appendChild(this.domNode);

			this.inherited(arguments);

			this.connect(this, "onExecute", "hide");
			this.connect(this, "onCancel", "hide");
			this._modalconnects = [];
		},

		onLoad: function(){
			// summary:
			//		Called when data has been loaded from an href.
			//		Unlike most other callbacks, this function can be connected to (via `dojo.connect`)
			//		but should *not* be overriden.
			// tags:
			//		callback

			// when href is specified we need to reposition the dialog after the data is loaded
			this._position();
			this.inherited(arguments);
		},

		_endDrag: function(e){
			// summary:
			//		Called after dragging the Dialog. Calculates the relative offset
			//		of the Dialog in relation to the viewport.
			// tags:
			//		private
			if(e && e.node && e.node === this.domNode){
				var vp = dijit.getViewport();
				var p = e._leftTop || dojo.coords(e.node,true);
				this._relativePosition = {
					t: p.t - vp.t,
					l: p.l - vp.l
				}
			}
		},

		_setup: function(){
			// summary:
			//		Stuff we need to do before showing the Dialog for the first
			//		time (but we defer it until right beforehand, for
			//		performance reasons).
			// tags:
			//		private

			var node = this.domNode;

			if(this.titleBar && this.draggable){
				this._moveable = (dojo.isIE == 6) ?
					new dojo.dnd.TimedMoveable(node, { handle: this.titleBar }) :	// prevent overload, see #5285
					new dojo.dnd.Moveable(node, { handle: this.titleBar, timeout: 0 });
				dojo.subscribe("/dnd/move/stop",this,"_endDrag");
			}else{
				dojo.addClass(node,"dijitDialogFixed");
			}

			var underlayAttrs = {
				dialogId: this.id,
				"class": dojo.map(this["class"].split(/\s/), function(s){ return s+"_underlay"; }).join(" ")
			};

			var underlay = dijit._underlay;
			if(!underlay){
				underlay = dijit._underlay = new dijit.DialogUnderlay(underlayAttrs);
			}

			this._fadeIn = dojo.fadeIn({
				node: node,
				duration: this.duration,
				beforeBegin: function(){
					underlay.attr(underlayAttrs);
					underlay.show();
				},
				onEnd:	dojo.hitch(this, function(){
					if(this.autofocus){
						// find focusable Items each time dialog is shown since if dialog contains a widget the
						// first focusable items can change
						this._getFocusItems(this.domNode);
						dijit.focus(this._firstFocusItem);
					}
				})
			 });

			this._fadeOut = dojo.fadeOut({
				node: node,
				duration: this.duration,
				onEnd: function(){
					node.style.visibility="hidden";
					node.style.top = "-9999px";
					dijit._underlay.hide();
				}
			 });
		},

		uninitialize: function(){
			var wasPlaying = false;
			if(this._fadeIn && this._fadeIn.status() == "playing"){
				wasPlaying = true;
				this._fadeIn.stop();
			}
			if(this._fadeOut && this._fadeOut.status() == "playing"){
				wasPlaying = true;
				this._fadeOut.stop();
			}
			if(this.open || wasPlaying){
				dijit._underlay.hide();
			}
			if(this._moveable){
				this._moveable.destroy();
			}
		},

		_size: function(){
			// summary:
			// 		Make sure the dialog is small enough to fit in viewport.
			// tags:
			//		private

			var mb = dojo.marginBox(this.domNode);
			var viewport = dijit.getViewport();
			if(mb.w >= viewport.w || mb.h >= viewport.h){
				dojo.style(this.containerNode, {
					width: Math.min(mb.w, Math.floor(viewport.w * 0.75))+"px",
					height: Math.min(mb.h, Math.floor(viewport.h * 0.75))+"px",
					overflow: "auto",
					position: "relative"	// workaround IE bug moving scrollbar or dragging dialog
				});
			}
		},

		_position: function(){
			// summary:
			//		Position modal dialog in the viewport. If no relative offset
			//		in the viewport has been determined (by dragging, for instance),
			//		center the node. Otherwise, use the Dialog's stored relative offset,
			//		and position the node to top: left: values based on the viewport.
			// tags:
			//		private
			if(!dojo.hasClass(dojo.body(),"dojoMove")){

				var node = this.domNode;
				var viewport = dijit.getViewport();
				var p = this._relativePosition;
				var mb = p ? null : dojo.marginBox(node);
				dojo.style(node,{
					left: Math.floor(viewport.l + (p ? p.l : (viewport.w - mb.w) / 2)) + "px",
					top: Math.floor(viewport.t + (p ? p.t : (viewport.h - mb.h) / 2)) + "px"
				});
			}

		},

		_onKey: function(/*Event*/ evt){
			// summary:
			//		Handles the keyboard events for accessibility reasons
			// tags:
			//		private
			if(evt.charOrCode){
				var dk = dojo.keys;
				var node = evt.target;
				if (evt.charOrCode === dk.TAB){
					this._getFocusItems(this.domNode);
				}
				var singleFocusItem = (this._firstFocusItem == this._lastFocusItem);
				// see if we are shift-tabbing from first focusable item on dialog
				if(node == this._firstFocusItem && evt.shiftKey && evt.charOrCode === dk.TAB){
					if(!singleFocusItem){
						dijit.focus(this._lastFocusItem); // send focus to last item in dialog
					}
					dojo.stopEvent(evt);
				}else if(node == this._lastFocusItem && evt.charOrCode === dk.TAB && !evt.shiftKey){
					if (!singleFocusItem){
						dijit.focus(this._firstFocusItem); // send focus to first item in dialog
					}
					dojo.stopEvent(evt);
				}else{
					// see if the key is for the dialog
					while(node){
						if(node == this.domNode){
							if(evt.charOrCode == dk.ESCAPE){
								this.onCancel();
							}else{
								return; // just let it go
							}
						}
						node = node.parentNode;
					}
					// this key is for the disabled document window
					if(evt.charOrCode !== dk.TAB){ // allow tabbing into the dialog for a11y
						dojo.stopEvent(evt);
					// opera won't tab to a div
					}else if(!dojo.isOpera){
						try{
							this._firstFocusItem.focus();
						}catch(e){ /*squelch*/ }
					}
				}
			}
		},

		show: function(){
			// summary:
			//		Display the dialog
			if(this.open){ return; }

			// first time we show the dialog, there's some initialization stuff to do
			if(!this._alreadyInitialized){
				this._setup();
				this._alreadyInitialized=true;
			}

			if(this._fadeOut.status() == "playing"){
				this._fadeOut.stop();
			}

			this._modalconnects.push(dojo.connect(window, "onscroll", this, "layout"));
			this._modalconnects.push(dojo.connect(window, "onresize", this, "layout"));
			this._modalconnects.push(dojo.connect(dojo.doc.documentElement, "onkeypress", this, "_onKey"));

			dojo.style(this.domNode, {
				opacity:0,
				visibility:""
			});
			dojo.style(this.containerNode, { // reset width and height so that _size():marginBox works correctly
				width:"auto",
				height:"auto"
			});

			this.open = true;
			this._onShow(); // lazy load trigger

			this._size();
			this._position();

			this._fadeIn.play();

			this._savedFocus = dijit.getFocus(this);
		},

		hide: function(){
			// summary:
			//		Hide the dialog

			// if we haven't been initialized yet then we aren't showing and we can just return
			if(!this._alreadyInitialized){
				return;
			}

			if(this._fadeIn.status() == "playing"){
				this._fadeIn.stop();
			}
			this._fadeOut.play();

			if (this._scrollConnected){
				this._scrollConnected = false;
			}
			dojo.forEach(this._modalconnects, dojo.disconnect);
			this._modalconnects = [];
			if(this.refocus){
				this.connect(this._fadeOut,"onEnd",dojo.hitch(dijit,"focus",this._savedFocus));
			}
			if(this._relativePosition){
				delete this._relativePosition;
			}
			this.open = false;
		},

		layout: function() {
			// summary:
			//		Position the Dialog and the underlay
			// tags:
			//		private
			if(this.domNode.style.visibility != "hidden"){
				dijit._underlay.layout();
				this._position();
			}
		},

		destroy: function(){
			dojo.forEach(this._modalconnects, dojo.disconnect);
			if(this.refocus && this.open){
				setTimeout(dojo.hitch(dijit,"focus",this._savedFocus), 25);
			}
			this.inherited(arguments);
		},

		_onCloseEnter: function(){
			// summary:
			//		Called when user hovers over close icon
			// tags:
			//		private
			dojo.addClass(this.closeButtonNode, "dijitDialogCloseIcon-hover");
		},

		_onCloseLeave: function(){
			// summary:
			//		Called when user stops hovering over close icon
			// tags:
			//		private
			dojo.removeClass(this.closeButtonNode, "dijitDialogCloseIcon-hover");
		}
	}
);

// For back-compat.  TODO: remove in 2.0
dojo.require("dijit.TooltipDialog");

<commit_msg>Ignore spurious resize events on IE, for performance but more importantly to prevent a browser hang.<commit_after>dojo.provide("dijit.Dialog");

dojo.require("dojo.dnd.move");
dojo.require("dojo.dnd.TimedMoveable");
dojo.require("dojo.fx");

dojo.require("dijit._Widget");
dojo.require("dijit._Templated");
dojo.require("dijit.form._FormMixin");
dojo.require("dijit._DialogMixin");
dojo.require("dijit.DialogUnderlay");
dojo.require("dijit.layout.ContentPane");
dojo.requireLocalization("dijit", "common");

/*=====
dijit._underlay = function(kwArgs){
	// summary:
	//		A shared instance of a `dijit.DialogUnderlay`
	//
	// description:
	//		A shared instance of a `dijit.DialogUnderlay` created and
	//		used by `dijit.Dialog`, though never created until some Dialog
	//		or subclass thereof is shown.
};
=====*/

dojo.declare(
	"dijit.Dialog",
	[dijit.layout.ContentPane, dijit._Templated, dijit.form._FormMixin, dijit._DialogMixin],
	{
		// summary:
		//		A modal dialog Widget
		//
		// description:
		//		Pops up a modal dialog window, blocking access to the screen
		//		and also graying out the screen Dialog is extended from
		//		ContentPane so it supports all the same parameters (href, etc.)
		//
		// example:
		// |

		//
		// example:
		// |	var foo = new dijit.Dialog({ title: "test dialog", content: "test content" };
		// |	dojo.body().appendChild(foo.domNode);
		// |	foo.startup();

		templateString: null,
		templatePath: dojo.moduleUrl("dijit", "templates/Dialog.html"),
		attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, {
			title: [
				{ node: "titleNode", type: "innerHTML" },
				{ node: "titleBar", type: "attribute" }
			]
		}),

		// open: Boolean
		//		True if Dialog is currently displayed on screen.
		open: false,

		// duration: Integer
		//		The time in milliseconds it takes the dialog to fade in and out
		duration: dijit.defaultDuration,

		// refocus: Boolean
		// 		A Toggle to modify the default focus behavior of a Dialog, which
		// 		is to re-focus the element which had focus before being opened.
		//		False will disable refocusing. Default: true
		refocus: true,

		// autofocus: Boolean
		// 		A Toggle to modify the default focus behavior of a Dialog, which
		// 		is to focus on the first dialog element after opening the dialog.
		//		False will disable autofocusing. Default: true
		autofocus: true,

		// _firstFocusItem: [private] [readonly] DomNode
		//		The pointer to the first focusable node in the dialog.
		//		Set by `dijit._DialogMixin._getFocusItems`.
		_firstFocusItem: null,

		// _lastFocusItem: [private] [readonly] DomNode
		//		The pointer to which node has focus prior to our dialog.
		//		Set by `dijit._DialogMixin._getFocusItems`.
		_lastFocusItem: null,

		// doLayout: [protected] Boolean
		//		Don't change this parameter from the default value.
		//		This ContentPane parameter doesn't make sense for Dialog, since Dialog
		//		is never a child of a layout container, nor can you specify the size of
		//		Dialog in order to control the size of an inner widget.
		doLayout: false,

		// draggable: Boolean
		//		Toggles the moveable aspect of the Dialog. If true, Dialog
		//		can be dragged by it's title. If false it will remain centered
		//		in the viewport.
		draggable: true,

		postMixInProperties: function(){
			var _nlsResources = dojo.i18n.getLocalization("dijit", "common");
			dojo.mixin(this, _nlsResources);
			this.inherited(arguments);
		},

		postCreate: function(){
			dojo.style(this.domNode, {
				visibility:"hidden",
				position:"absolute",
				display:"",
				top:"-9999px"
			});
			dojo.body().appendChild(this.domNode);

			this.inherited(arguments);

			this.connect(this, "onExecute", "hide");
			this.connect(this, "onCancel", "hide");
			this._modalconnects = [];
		},

		onLoad: function(){
			// summary:
			//		Called when data has been loaded from an href.
			//		Unlike most other callbacks, this function can be connected to (via `dojo.connect`)
			//		but should *not* be overriden.
			// tags:
			//		callback

			// when href is specified we need to reposition the dialog after the data is loaded
			this._position();
			this.inherited(arguments);
		},

		_endDrag: function(e){
			// summary:
			//		Called after dragging the Dialog. Calculates the relative offset
			//		of the Dialog in relation to the viewport.
			// tags:
			//		private
			if(e && e.node && e.node === this.domNode){
				var vp = dijit.getViewport();
				var p = e._leftTop || dojo.coords(e.node,true);
				this._relativePosition = {
					t: p.t - vp.t,
					l: p.l - vp.l
				}
			}
		},

		_setup: function(){
			// summary:
			//		Stuff we need to do before showing the Dialog for the first
			//		time (but we defer it until right beforehand, for
			//		performance reasons).
			// tags:
			//		private

			var node = this.domNode;

			if(this.titleBar && this.draggable){
				this._moveable = (dojo.isIE == 6) ?
					new dojo.dnd.TimedMoveable(node, { handle: this.titleBar }) :	// prevent overload, see #5285
					new dojo.dnd.Moveable(node, { handle: this.titleBar, timeout: 0 });
				dojo.subscribe("/dnd/move/stop",this,"_endDrag");
			}else{
				dojo.addClass(node,"dijitDialogFixed");
			}

			var underlayAttrs = {
				dialogId: this.id,
				"class": dojo.map(this["class"].split(/\s/), function(s){ return s+"_underlay"; }).join(" ")
			};

			var underlay = dijit._underlay;
			if(!underlay){
				underlay = dijit._underlay = new dijit.DialogUnderlay(underlayAttrs);
			}

			this._fadeIn = dojo.fadeIn({
				node: node,
				duration: this.duration,
				beforeBegin: function(){
					underlay.attr(underlayAttrs);
					underlay.show();
				},
				onEnd:	dojo.hitch(this, function(){
					if(this.autofocus){
						// find focusable Items each time dialog is shown since if dialog contains a widget the
						// first focusable items can change
						this._getFocusItems(this.domNode);
						dijit.focus(this._firstFocusItem);
					}
				})
			 });

			this._fadeOut = dojo.fadeOut({
				node: node,
				duration: this.duration,
				onEnd: function(){
					node.style.visibility="hidden";
					node.style.top = "-9999px";
					dijit._underlay.hide();
				}
			 });
		},

		uninitialize: function(){
			var wasPlaying = false;
			if(this._fadeIn && this._fadeIn.status() == "playing"){
				wasPlaying = true;
				this._fadeIn.stop();
			}
			if(this._fadeOut && this._fadeOut.status() == "playing"){
				wasPlaying = true;
				this._fadeOut.stop();
			}
			if(this.open || wasPlaying){
				dijit._underlay.hide();
			}
			if(this._moveable){
				this._moveable.destroy();
			}
		},

		_size: function(){
			// summary:
			// 		Make sure the dialog is small enough to fit in viewport.
			// tags:
			//		private

			var mb = dojo.marginBox(this.domNode);
			var viewport = dijit.getViewport();
			if(mb.w >= viewport.w || mb.h >= viewport.h){
				dojo.style(this.containerNode, {
					width: Math.min(mb.w, Math.floor(viewport.w * 0.75))+"px",
					height: Math.min(mb.h, Math.floor(viewport.h * 0.75))+"px",
					overflow: "auto",
					position: "relative"	// workaround IE bug moving scrollbar or dragging dialog
				});
			}
		},

		_position: function(){
			// summary:
			//		Position modal dialog in the viewport. If no relative offset
			//		in the viewport has been determined (by dragging, for instance),
			//		center the node. Otherwise, use the Dialog's stored relative offset,
			//		and position the node to top: left: values based on the viewport.
			// tags:
			//		private
			if(!dojo.hasClass(dojo.body(),"dojoMove")){
				var node = this.domNode;
				var viewport = dijit.getViewport();
					var p = this._relativePosition;
					var mb = p ? null : dojo.marginBox(node);
					dojo.style(node,{
						left: Math.floor(viewport.l + (p ? p.l : (viewport.w - mb.w) / 2)) + "px",
						top: Math.floor(viewport.t + (p ? p.t : (viewport.h - mb.h) / 2)) + "px"
					});
				}

		},

		_onKey: function(/*Event*/ evt){
			// summary:
			//		Handles the keyboard events for accessibility reasons
			// tags:
			//		private
			if(evt.charOrCode){
				var dk = dojo.keys;
				var node = evt.target;
				if (evt.charOrCode === dk.TAB){
					this._getFocusItems(this.domNode);
				}
				var singleFocusItem = (this._firstFocusItem == this._lastFocusItem);
				// see if we are shift-tabbing from first focusable item on dialog
				if(node == this._firstFocusItem && evt.shiftKey && evt.charOrCode === dk.TAB){
					if(!singleFocusItem){
						dijit.focus(this._lastFocusItem); // send focus to last item in dialog
					}
					dojo.stopEvent(evt);
				}else if(node == this._lastFocusItem && evt.charOrCode === dk.TAB && !evt.shiftKey){
					if (!singleFocusItem){
						dijit.focus(this._firstFocusItem); // send focus to first item in dialog
					}
					dojo.stopEvent(evt);
				}else{
					// see if the key is for the dialog
					while(node){
						if(node == this.domNode){
							if(evt.charOrCode == dk.ESCAPE){
								this.onCancel();
							}else{
								return; // just let it go
							}
						}
						node = node.parentNode;
					}
					// this key is for the disabled document window
					if(evt.charOrCode !== dk.TAB){ // allow tabbing into the dialog for a11y
						dojo.stopEvent(evt);
					// opera won't tab to a div
					}else if(!dojo.isOpera){
						try{
							this._firstFocusItem.focus();
						}catch(e){ /*squelch*/ }
					}
				}
			}
		},

		show: function(){
			// summary:
			//		Display the dialog
			if(this.open){ return; }

			// first time we show the dialog, there's some initialization stuff to do
			if(!this._alreadyInitialized){
				this._setup();
				this._alreadyInitialized=true;
			}

			if(this._fadeOut.status() == "playing"){
				this._fadeOut.stop();
			}

			this._modalconnects.push(dojo.connect(window, "onscroll", this, "layout"));
			this._modalconnects.push(dojo.connect(window, "onresize", this, function(){
				// IE gives spurious resize events and can actually get stuck
				// in an infinite loop if we don't ignore them
				var viewport = dijit.getViewport();
				if(!this._oldViewport ||
						viewport.h != this._oldViewport.h ||
						viewport.w != this._oldViewport.w){
					this.layout();
					this._oldViewport = viewport;
				}
			}));
			this._modalconnects.push(dojo.connect(dojo.doc.documentElement, "onkeypress", this, "_onKey"));

			dojo.style(this.domNode, {
				opacity:0,
				visibility:""
			});
			dojo.style(this.containerNode, { // reset width and height so that _size():marginBox works correctly
				width:"auto",
				height:"auto"
			});

			this.open = true;
			this._onShow(); // lazy load trigger

			this._size();
			this._position();

			this._fadeIn.play();

			this._savedFocus = dijit.getFocus(this);
		},

		hide: function(){
			// summary:
			//		Hide the dialog

			// if we haven't been initialized yet then we aren't showing and we can just return
			if(!this._alreadyInitialized){
				return;
			}

			if(this._fadeIn.status() == "playing"){
				this._fadeIn.stop();
			}
			this._fadeOut.play();

			if (this._scrollConnected){
				this._scrollConnected = false;
			}
			dojo.forEach(this._modalconnects, dojo.disconnect);
			this._modalconnects = [];
			if(this.refocus){
				this.connect(this._fadeOut,"onEnd",dojo.hitch(dijit,"focus",this._savedFocus));
			}
			if(this._relativePosition){
				delete this._relativePosition;
			}
			this.open = false;
		},

		layout: function() {
			// summary:
			//		Position the Dialog and the underlay
			// tags:
			//		private
			if(this.domNode.style.visibility != "hidden"){
				dijit._underlay.layout();
				this._position();
			}
		},

		destroy: function(){
			dojo.forEach(this._modalconnects, dojo.disconnect);
			if(this.refocus && this.open){
				setTimeout(dojo.hitch(dijit,"focus",this._savedFocus), 25);
			}
			this.inherited(arguments);
		},

		_onCloseEnter: function(){
			// summary:
			//		Called when user hovers over close icon
			// tags:
			//		private
			dojo.addClass(this.closeButtonNode, "dijitDialogCloseIcon-hover");
		},

		_onCloseLeave: function(){
			// summary:
			//		Called when user stops hovering over close icon
			// tags:
			//		private
			dojo.removeClass(this.closeButtonNode, "dijitDialogCloseIcon-hover");
		}
	}
);

// For back-compat.  TODO: remove in 2.0
dojo.require("dijit.TooltipDialog");

<|endoftext|>