aboutsummaryrefslogtreecommitdiffstats
path: root/docs/src/writer.js
blob: c9089be09a96328274b71fa059edca66c37bd29d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
/**
 * All writing related code here. This is so that we can separate the async code from sync code
 * for testability
 */
var pathUtils = require('path');
var qfs = require('q-fs');
var Q = require('qq');
var OUTPUT_DIR = pathUtils.join('build','docs');
var TEMPLATES_DIR = pathUtils.join('docs','src','templates');
var fs = require('fs');

exports.output = output;
function output(file, content) {
  var fullPath = pathUtils.join(OUTPUT_DIR,file);
  var dir = pathUtils.dirname(fullPath);
  return Q.when(exports.makeDir(dir), function(error) {
    qfs.write(fullPath, exports.toString(content));
  });
}

//recursively create directory
exports.makeDir = function(p) {
  p = pathUtils.normalize(p);
  var parts = p.split(pathUtils.sep);
  var path = ".";

  // Recursively rebuild directory structure
  return qfs.exists(p).
      then(function createPart(exists) {
        if(!exists && parts.length) {
          path = pathUtils.join(path, parts.shift());
          return qfs.exists(path).then(function(exists) {
            if (!exists) {
              return qfs.makeDirectory(path).then(createPart, createPart);
            } else {
              return createPart();
            }
          });
        }
      });
};

exports.copyTemplate = function(filename) {
  // Don't need to normalize here as `exports.copy` will do it for us
  return exports.copy(pathUtils.join(TEMPLATES_DIR,filename), filename);
};

/* Copy files from one place to another.
 * @param from{string} path of the source file to be copied
 * @param to{string} path of where the copied file should be stored
 * @param  transform{function=} transfromation function to be applied before return
 */
exports.copy = function(from, to, transform) {
  from = pathUtils.normalize(from);
  to = pathUtils.normalize(to);

  // We have to use binary reading, Since some characters are unicode.
  return qfs.read(from, 'b').then(function(content) {
    if (transform) {
      content = transform.call(null, content.toString(), from, to, transform);
    }
    return output(to, content);
  });
};


exports.symlink = symlink;
function symlink(from, to, type) {
  // qfs will normalize the path arguments for us here
  return qfs.exists(to).then(function(exists) {
    if (!exists) {
      return qfs.symbolicLink(to, from, type);
    }
  });
}


exports.symlinkTemplate = symlinkTemplate;
function symlinkTemplate(filename, type) {
  // pathUtils.join will normalize the filename for us
  var dest = pathUtils.join(OUTPUT_DIR, filename),
      dirDepth = dest.split(pathUtils.sep).length,
      src = pathUtils.join(Array(dirDepth).join('..' + pathUtils.sep), TEMPLATES_DIR, filename);
  return symlink(src, dest, type);
}


/* Replace placeholders in content accordingly
 * @param content{string} content to be modified
 * @param replacements{obj} key and value pairs in which key will be replaced with value in content
 */
exports.replace = function(content, replacements) {
  for(var key in replacements) {
    content = content.replace(key, replacements[key]);
  }
  return content;
};

exports.copyDir = function copyDir(from, to) {
  from = pathUtils.normalize(from);
  to = pathUtils.normalize(to);
  return qfs.listTree(from).then(function(files) {
    files.forEach(function(file) {
      var path = to ? file.replace(from, to) : from;
      // Not sure why this next line is here...
      path = path.replace('/docs/build', '');
      exports.copy(file, path);
    });
  });
};

exports.merge = function(srcs, to) {
  // pathUtils.join will normalize each of the srcs inside the mapping
  to = pathUtils.normalize(to);
  return merge(srcs.map(function(src) { return pathUtils.join(TEMPLATES_DIR, src); }), to);
};

function merge(srcs, to) {
  var contents = [];
  //Sequentially read file
  var done;
  srcs.forEach(function(src) {
    done = Q.when(done, function(content) {
      if(content) contents.push(content);
      return qfs.read(src, 'b');
    });
  });

  // write to file
  return Q.when(done, function(content) {
    contents.push(content);
    return output(to, contents.join('\n'));
  });
}

//----------------------- Synchronous Methods ----------------------------------

exports.toString = function toString(obj) {
  switch (typeof obj) {
  case 'string':
    return obj;
  case 'object':
    if (obj instanceof Array) {
      obj.forEach(function(value, key) {
        obj[key] = toString(value);
      });
      return obj.join('');
    } else if (obj.constructor.name == 'Buffer'){
      // do nothing it is Buffer Object
    } else {
      return JSON.stringify(obj);
    }
  }
  return obj;
};


function noop() {}

0) { callback(); } else { outstandingRequestCallbacks.push(callback); } }; ////////////////////////////////////////////////////////////// // Poll Watcher API ////////////////////////////////////////////////////////////// var pollFns = []; /** * @workInProgress * @ngdoc method * @name angular.service.$browser#poll * @methodOf angular.service.$browser */ self.poll = function() { forEach(pollFns, function(pollFn){ pollFn(); }); }; /** * @workInProgress * @ngdoc method * @name angular.service.$browser#addPollFn * @methodOf angular.service.$browser * * @param {function()} fn Poll function to add * * @description * Adds a function to the list of functions that poller periodically executes * * @returns {function()} the added function */ self.addPollFn = function(fn) { pollFns.push(fn); return fn; }; /** * @workInProgress * @ngdoc method * @name angular.service.$browser#startPoller * @methodOf angular.service.$browser * * @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. */ self.startPoller = function(interval, setTimeout) { (function check(){ self.poll(); setTimeout(check, interval); })(); }; ////////////////////////////////////////////////////////////// // URL API ////////////////////////////////////////////////////////////// /** * @workInProgress * @ngdoc method * @name angular.service.$browser#setUrl * @methodOf angular.service.$browser * * @param {string} url New url * * @description * Sets browser's url */ self.setUrl = function(url) { var existingURL = location.href; if (!existingURL.match(/#/)) existingURL += '#'; if (!url.match(/#/)) url += '#'; location.href = url; }; /** * @workInProgress * @ngdoc method * @name angular.service.$browser#getUrl * @methodOf angular.service.$browser * * @description * Get current browser's url * * @returns {string} Browser's url */ self.getUrl = function() { return location.href; }; /** * @workInProgress * @ngdoc method * @name angular.service.$browser#onHashChange * @methodOf angular.service.$browser * * @description * Detects if browser support onhashchange events and register a listener otherwise registers * $browser poller. The `listener` will then get called when the hash changes. * * The listener gets called with either HashChangeEvent object or simple object that also contains * `oldURL` and `newURL` properties. * * NOTE: this api is intended for use only by the $location service. Please use the * {@link angular.service.$location $location service} to monitor hash changes in angular apps. * * @param {function(event)} listener Listener function to be called when url hash changes. * @return {function()} Returns the registered listener fn - handy if the fn is anonymous. */ self.onHashChange = function(listener) { if ('onhashchange' in window) { jqLite(window).bind('hashchange', listener); } else { var lastBrowserUrl = self.getUrl(); self.addPollFn(function() { if (lastBrowserUrl != self.getUrl()) { listener(); lastBrowserUrl = self.getUrl(); } }); } return listener; }; ////////////////////////////////////////////////////////////// // Cookies API ////////////////////////////////////////////////////////////// var rawDocument = document[0]; var lastCookies = {}; var lastCookieString = ''; /** * @workInProgress * @ngdoc method * @name angular.service.$browser#cookies * @methodOf angular.service.$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: * <ul> * <li>cookies() -> hash of all cookies, this is NOT a copy of the internal state, so do not modify it</li> * <li>cookies(name, value) -> set name to value, if value is undefined delete the cookie</li> * <li>cookies(name) -> the same as (name, undefined) == DELETES (no one calls it right now that way)</li> * </ul> * * @returns {Object} Hash of all cookies (if called without any parameter) */ self.cookies = function (name, value) { var cookieLength, cookieArray, cookie, i, keyValue, index; if (name) { if (value === _undefined) { rawDocument.cookie = escape(name) + "=;expires=Thu, 01 Jan 1970 00:00:00 GMT"; } else { if (isString(value)) { rawDocument.cookie = escape(name) + '=' + escape(value); cookieLength = name.length + value.length + 1; if (cookieLength > 4096) { $log.warn("Cookie '"+ name +"' possibly not set or overflowed because it was too large ("+ cookieLength + " > 4096 bytes)!"); } if (lastCookies.length > 20) { $log.warn("Cookie '"+ name +"' possibly not set or overflowed because too many cookies " + "were already set (" + lastCookies.length + " > 20 )"); } } } } else { if (rawDocument.cookie !== lastCookieString) { lastCookieString = rawDocument.cookie; cookieArray = lastCookieString.split("; "); lastCookies = {}; for (i = 0; i < cookieArray.length; i++) { cookie = cookieArray[i]; index = cookie.indexOf('='); if (index > 0) { //ignore nameless cookies lastCookies[unescape(cookie.substring(0, index))] = unescape(cookie.substring(index + 1)); } } } return lastCookies; } }; /** * @workInProgress * @ngdoc method * @name angular.service.$browser#defer * @methodOf angular.service.$browser * @param {function()} fn A function, who's execution should be defered. * @param {number=} [delay=0] of milliseconds to defer the function execution. * * @description * Executes a fn asynchroniously via `setTimeout(fn, delay)`. * * Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using * `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed via * `$browser.defer.flush()`. * */ self.defer = function(fn, delay) { outstandingRequestCount++; setTimeout(function() { completeOutstandingRequest(fn); }, delay || 0); }; ////////////////////////////////////////////////////////////// // Misc API ////////////////////////////////////////////////////////////// var hoverListener = noop; /** * @workInProgress * @ngdoc method * @name angular.service.$browser#hover * @methodOf angular.service.$browser * * @description * Set hover listener. * * @param {function(Object, boolean)} listener Function that will be called when a hover event * occurs. */ self.hover = function(listener) { hoverListener = listener; }; /** * @workInProgress * @ngdoc method * @name angular.service.$browser#bind * @methodOf angular.service.$browser * * @description * Register hover function to real browser */ self.bind = function() { document.bind("mouseover", function(event){ hoverListener(jqLite(msie ? event.srcElement : event.target), true); return true; }); document.bind("mouseleave mouseout click dblclick keypress keyup", function(event){ hoverListener(jqLite(event.target), false); return true; }); }; /** * @workInProgress * @ngdoc method * @name angular.service.$browser#addCss * @methodOf angular.service.$browser * * @param {string} url Url to css file * @description * Adds a stylesheet tag to the head. */ self.addCss = function(url) { var link = jqLite(rawDocument.createElement('link')); link.attr('rel', 'stylesheet'); link.attr('type', 'text/css'); link.attr('href', url); body.append(link); }; /** * @workInProgress * @ngdoc method * @name angular.service.$browser#addJs * @methodOf angular.service.$browser * * @param {string} url Url to js file * @param {string=} dom_id Optional id for the script tag * * @description * Adds a script tag to the head. */ self.addJs = function(url, dom_id) { var script = jqLite(rawDocument.createElement('script')); script.attr('type', 'text/javascript'); script.attr('src', url); if (dom_id) script.attr('id', dom_id); body.append(script); }; }