'use strict';
//////////////////////////////
// Browser
//////////////////////////////
var XHR = window.XMLHttpRequest || function() {
try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch (e1) {}
try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); } catch (e2) {}
try { return new ActiveXObject("Msxml2.XMLHTTP"); } catch (e3) {}
throw new Error("This browser does not support XMLHttpRequest.");
};
/**
* @ngdoc object
* @name angular.module.ng.$browser
* @requires $log
* @description
* This object has two goals:
*
* - hide all the global state in the browser caused by the window object
* - abstract away all the browser specific features and inconsistencies
*
* For tests we provide {@link angular.module.ngMock.$browser mock implementation} of the `$browser`
* service, which can be used for convenient testing of the application without the interaction with
* the real browser apis.
*/
/**
* @param {object} window The global window object.
* @param {object} document jQuery wrapped document.
* @param {object} body jQuery wrapped document.body.
* @param {function()} XHR XMLHttpRequest constructor.
* @param {object} $log console.log or an object with the same interface.
* @param {object} $sniffer $sniffer service
*/
function Browser(window, document, body, XHR, $log, $sniffer) {
var self = this,
rawDocument = document[0],
location = window.location,
history = window.history,
setTimeout = window.setTimeout,
clearTimeout = window.clearTimeout,
pendingDeferIds = {};
self.isMock = false;
//////////////////////////////////////////////////////////////
// XHR API
//////////////////////////////////////////////////////////////
var idCounter = 0;
var outstandingRequestCount = 0;
var outstandingRequestCallbacks = [];
/**
* Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks`
* counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed.
*/
function completeOutstandingRequest(fn) {
try {
fn.apply(null, sliceArgs(arguments, 1));
} finally {
outstandingRequestCount--;
if (outstandingRequestCount === 0) {
while(outstandingRequestCallbacks.length) {
try {
outstandingRequestCallbacks.pop()();
} catch (e) {
$log.error(e);
}
}
}
}
}
/**
* @ngdoc method
* @name angular.module.ng.$browser#xhr
* @methodOf angular.module.ng.$browser
*
* @param {string} method Requested method (get|post|put|delete|head|json)
* @param {string} url Requested url
* @param {?string} post Post data to send (null if nothing to post)
* @param {function(number, string)} callback Function that will be called on response
* @param {object=} header additional HTTP headers to send with XHR.
* Standard headers are:
*
* - Content-Type: application/x-www-form-urlencoded
* - Accept: application/json, text/plain, */*
* - X-Requested-With: XMLHttpRequest
*
*
* @returns {XMLHttpRequest|undefined} Raw XMLHttpRequest object or undefined when JSONP method
*
* @description
* Send ajax request
*
* TODO(vojta): change signature of this method to (method, url, data, headers, callback)
*/
self.xhr = function(method, url, post, callback, headers) {
outstandingRequestCount ++;
if (lowercase(method) == 'json') {
var callbackId = ("angular_" + Math.random() + '_' + (idCounter++)).replace(/\d\./, '');
window[callbackId] = function(data) {
window[callbackId].data = data;
};
var script = self.addJs(url.replace('JSON_CALLBACK', callbackId), function() {
if (window[callbackId].data) {
completeOutstandingRequest(callback, 200, window[callbackId].data);
} else {
completeOutstandingRequest(callback);
}
delete window[callbackId];
body[0].removeChild(script);
});
} else {
var xhr = new XHR();
xhr.open(method, url, true);
forEach(headers, function(value, key) {
if (value) xhr.setRequestHeader(key, value);
});
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
// normalize IE bug (http://bugs.jquery.com/ticket/1450)
var status = xhr.status == 1223 ? 204 : xhr.status;
completeOutstandingRequest(callback, status, xhr.responseText);
}
};
xhr.send(post || '');
return xhr;
}
};
/**
* @private
* Note: this method is used only by scenario runner
* TODO(vojta): prefix this method with $$ ?
* @param {function()} callback Function that will be called when no outstanding request
*/
self.notifyWhenNoOutstandingRequests = function(callback) {
// force browser to execute all pollFns - this is needed so that cookies and other pollers fire
// at some deterministic time in respect to the test runner's actions. Leaving things up to the
// regular poller would result in flaky tests.
forEach(pollFns, function(pollFn){ pollFn(); });
if (outstandingRequestCount === 0) {
callback();
} else {
outstandingRequestCallbacks.push(callback);
}
};
//////////////////////////////////////////////////////////////
// Poll Watcher API
//////////////////////////////////////////////////////////////
var pollFns = [],
pollTimeout;
/**
* @ngdoc method
* @name angular.module.ng.$browser#addPollFn
* @methodOf angular.module.ng.$browser
*
* @param {function()} fn Poll function to add
*
* @description
* Adds a function to the list of functions that poller periodically executes,
* and starts polling if not started yet.
*
* @returns {function()} the added function
*/
self.addPollFn = function(fn) {
if (isUndefined(pollTimeout)) startPoller(100, setTimeout);
pollFns.push(fn);
return fn;
};
/**
* @param {number} interval How often should browser call poll functions (ms)
* @param {function()} setTimeout Reference to a real or fake `setTimeout` function.
*
* @description
* Configures the poller to run in the specified intervals, using the specified
* setTimeout fn and kicks it off.
*/
function startPoller(interval, setTimeout) {
(function check() {
forEach(pollFns, function(pollFn){ pollFn(); });
pollTimeout = setTimeout(check, interval);
})();
}
//////////////////////////////////////////////////////////////
// URL API
//////////////////////////////////////////////////////////////
var lastBrowserUrl = location.href;
/**
* @ngdoc method
* @name angular.module.ng.$browser#url
* @methodOf angular.module.ng.$browser
*
* @description
* GETTER:
* Without any argument, this method just returns current value of location.href.
*
* SETTER:
* With at least one argument, this method sets url to new value.
* If html5 history api supported, pushState/replaceState is used, otherwise
* location.href/location.replace is used.
* Returns its own instance to allow chaining
*
* NOTE: this api is intended for use only by the $location service. Please use the
* {@link angular.module.ng.$location $location service} to change url.
*
* @param {string} url New url (when used as setter)
* @param {boolean=} replace Should new url replace current history record ?
*/
self.url = function(url, replace) {
// setter
if (url) {
lastBrowserUrl = url;
if ($sniffer.history) {
if (replace) history.replaceState(null, '', url);
else history.pushState(null, '', url);
} else {
if (replace) location.replace(url);
else location.href = url;
}
return self;
// getter
} else {
return location.href;
}
};
var urlChangeListeners = [],
urlChangeInit = false;
function fireUrlChange() {
if (lastBrowserUrl == self.url()) return;
lastBrowserUrl = self.url();
forEach(urlChangeListeners, function(listener) {
listener(self.url());
});
}
/**
* @ngdoc method
* @name angular.module.ng.$browser#onUrlChange
* @methodOf angular.module.ng.$browser
* @TODO(vojta): refactor to use node's syntax for events
*
* @description
* Register callback function that will be called, when url changes.
*
* It's only called when the url is changed by outside of angular:
* - user types different url into address bar
* - user clicks on history (forward/back) button
* - user clicks on a link
*
* It's not called when url is changed by $browser.url() method
*
* The listener gets called with new url as parameter.
*
* NOTE: this api is intended for use only by the $location service. Please use the
* {@link angular.module.ng.$location $location service} to monitor url changes in angular apps.
*
* @param {function(string)} listener Listener function to be called when url changes.
* @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous.
*/
self.onUrlChange = function(callback) {
if (!urlChangeInit) {
// We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera)
// don't fire popstate when user change the address bar and don't fire hashchange when url
// changed by push/replaceState
// html5 history api - popstate event
if ($sniffer.history) jqLite(window).bind('popstate', fireUrlChange);
// hashchange event
if ($sniffer.hashchange) jqLite(window).bind('hashchange', fireUrlChange);
// polling
else self.addPollFn(fireUrlChange);
urlChangeInit = true;
}
urlChangeListeners.push(callback);
return callback;
};
//////////////////////////////////////////////////////////////
// Cookies API
//////////////////////////////////////////////////////////////
var lastCookies = {};
var lastCookieString = '';
/**
* @ngdoc method
* @name angular.module.ng.$browser#cookies
* @methodOf angular.module.ng.$browser
*
* @param {string=} name Cookie name
* @param {string=} value Cokkie value
*
* @description
* The cookies method provides a 'private' low level access to browser cookies.
* It is not meant to be used directly, use the $cookie service instead.
*
* The return values vary depending on the arguments that the method was called with as follows:
*
* - cookies() -> hash of all cookies, this is NOT a copy of the internal state, so do not modify it
* #: * `create` <URL> [`--autotools`|`--cmake`|`--meson`] [`--no-fetch`] [`--set-name` <name>] [`--set-version` <version>] [`--tap` <user>`/`<repo>]:
#: Generate a formula for the downloadable file at <URL> and open it in the editor.
#: Homebrew will attempt to automatically derive the formula name
#: and version, but if it fails, you'll have to make your own template. The `wget`
#: formula serves as a simple example. For the complete API have a look at
#:
#: <http://www.rubydoc.info/github/Homebrew/brew/master/Formula>
#:
#: If `--autotools` is passed, create a basic template for an Autotools-style build.
#: If `--cmake` is passed, create a basic template for a CMake-style build.
#: If `--meson` is passed, create a basic template for a Meson-style build.
#:
#: If `--no-fetch` is passed, Homebrew will not download <URL> to the cache and
#: will thus not add the SHA256 to the formula for you.
#:
#: The options `--set-name` and `--set-version` each take an argument and allow
#: you to explicitly set the name and version of the package you are creating.
#:
#: The option `--tap` takes a tap as its argument and generates the formula in
#: the specified tap.
require "formula"
require "blacklist"
require "digest"
require "erb"
module Homebrew
module_function
# Create a formula from a tarball URL
def create
# Allow searching MacPorts or Fink.
if ARGV.include? "--macports"
opoo "`brew create --macports` is deprecated; use `brew search --macports` instead"
exec_browser "https://www.macports.org/ports.php?by=name&substr=#{ARGV.next}"
elsif ARGV.include? "--fink"
opoo "`brew create --fink` is deprecated; use `brew search --fink` instead"
exec_browser "http://pdb.finkproject.org/pdb/browse.php?summary=#{ARGV.next}"
end
raise UsageError if ARGV.named.empty?
# Ensure that the cache exists so we can fetch the tarball
HOMEBREW_CACHE.mkpath
url = ARGV.named.first # Pull the first (and only) url from ARGV
version = ARGV.next if ARGV.include? "--set-version"
name = ARGV.next if ARGV.include? "--set-name"
tap = ARGV.next if ARGV.include? "--tap"
fc = FormulaCreator.new
fc.name = name
fc.version = version
fc.tap = Tap.fetch(tap || "homebrew/core")
raise TapUnavailableError, tap unless fc.tap.installed?
fc.url = url
fc.mode = if ARGV.include? "--cmake"
:cmake
elsif ARGV.include? "--autotools"
:autotools
elsif ARGV.include? "--meson"
:meson
end
if fc.name.nil? || fc.name.strip.empty?
stem = Pathname.new(url).stem
print "Formula name [#{stem}]: "
fc.name = __gets || stem
fc.update_path
end
# Don't allow blacklisted formula, or names that shadow aliases,
# unless --force is specified.
unless ARGV.force?
if msg = blacklisted?(fc.name)
raise "#{fc.name} is blacklisted for creation.\n#{msg}\nIf you really want to create this formula use --force."
end
if Formula.aliases.include? fc.name
realname = Formulary.canonical_name(fc.name)
raise <<-EOS.undent
The formula #{realname} is already aliased to #{fc.name}
Please check that you are not creating a duplicate.
To force creation use --force.
EOS
end
end
fc.generate!
puts "Please `brew audit --new-formula #{fc.name}` before submitting, thanks."
exec_editor fc.path
end
def __gets
gots = $stdin.gets.chomp
gots.empty? ? nil : gots
end
end
class FormulaCreator
attr_reader :url, :sha256
attr_accessor :name, :version, :tap, :path, :mode
def url=(url)
@url = url
path = Pathname.new(url)
if @name.nil?
case url
when %r{github\.com/\S+/(\S+)\.git}
@name = $1
@head = true
when %r{github\.com/\S+/(\S+)/archive/}
@name = $1
else
@name = path.basename.to_s[/(.*?)[-_.]?#{Regexp.escape(path.version.to_s)}/, 1]
end
end
update_path
if @version
@version = Version.create(@version)
else
@version = Version.detect(url, {})
end
end
def update_path
return if @name.nil? || @tap.nil?
@path = Formulary.path "#{@tap}/#{@name}"
end
def fetch?
!head? && !ARGV.include?("--no-fetch")
end
def head?
@head || ARGV.build_head?
end
def generate!
raise "#{path} already exists" if path.exist?
if version.nil? || version.null?
opoo "Version cannot be determined from URL."
puts "You'll need to add an explicit 'version' to the formula."
elsif fetch?
r = Resource.new
r.url(url)
r.version(version)
r.owner = self
@sha256 = r.fetch.sha256 if r.download_strategy == CurlDownloadStrategy
end
path.write ERB.new(template, nil, ">").result(binding)
end
def template; <<-EOS.undent
# Documentation: http://docs.brew.sh/Formula-Cookbook.html
# http://www.rubydoc.info/github/Homebrew/brew/master/Formula
# PLEASE REMOVE ALL GENERATED COMMENTS BEFORE SUBMITTING YOUR PULL REQUEST!
class #{Formulary.class_s(name)} < Formula
desc ""
homepage ""
<% if head? %>
head "#{url}"
<% else %>
url "#{url}"
<% unless version.nil? or version.detected_from_url? %>
version "#{version}"
<% end %>
sha256 "#{sha256}"
<% end %>
<% if mode == :cmake %>
depends_on "cmake" => :build
<% elsif mode == :meson %>
depends_on "meson" => :build
depends_on "ninja" => :build
<% elsif mode.nil? %>
# depends_on "cmake" => :build
<% end %>
def install
# ENV.deparallelize # if your formula fails when building in parallel
<% if mode == :cmake %>
system "cmake", ".", *std_cmake_args
<% elsif mode == :autotools %>
# Remove unrecognized options if warned by configure
system "./configure", "--disable-debug",
"--disable-dependency-tracking",
"--disable-silent-rules",
"--prefix=\#{prefix}"
<% elsif mode == :meson %>
mkdir "build" do
system "meson", "--prefix=\#{prefix}", ".."
system "ninja"
system "ninja", "test"
system "ninja", "install"
end
<% else %>
# Remove unrecognized options if warned by configure
system "./configure", "--disable-debug",
"--disable-dependency-tracking",
"--disable-silent-rules",
"--prefix=\#{prefix}"
# system "cmake", ".", *std_cmake_args
<% end %>
<% if mode != :meson %>
system "make", "install" # if this fails, try separate make/make install steps
<% end %>
end
test do
# `test do` will create, run in and delete a temporary directory.
#
# This test will fail and we won't accept that! It's enough to just replace
# "false" with the main program this formula installs, but it'd be nice if you
# were more thorough. Run the test with `brew test #{name}`. Options passed
# to `brew install` such as `--HEAD` also need to be provided to `brew test`.
#
# The installed folder is not in the path, so use the entire path to any
# executables being tested: `system "\#{bin}/program", "do", "something"`.
system "false"
end
end
EOS
end
end