dolt 0.13.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. data/Gemfile.lock +3 -3
  2. data/Readme.org +161 -0
  3. data/bin/dolt +23 -3
  4. data/dolt.gemspec +1 -1
  5. data/vendor/ui/.gitignore +3 -1
  6. data/vendor/ui/.gitmodules +21 -18
  7. data/vendor/ui/build +18 -19
  8. data/vendor/ui/buster.js +20 -5
  9. data/vendor/ui/css/gitorious.css +19 -3
  10. data/vendor/ui/dist/gts-ui-deps.js +1 -27
  11. data/vendor/ui/images/gitorious.png +0 -0
  12. data/vendor/ui/js/src/app.js +345 -0
  13. data/vendor/ui/js/src/components/abbrev.js +25 -0
  14. data/vendor/ui/js/src/components/commit-linker.js +26 -0
  15. data/vendor/ui/js/src/components/profile-menu.js +34 -0
  16. data/vendor/ui/js/{components → src/components}/ref-selector.js +32 -4
  17. data/vendor/ui/js/src/components/tree-history.js +138 -0
  18. data/vendor/ui/js/src/components/url.js +64 -0
  19. data/vendor/ui/js/{gitorious.js → src/gitorious.js} +19 -0
  20. data/vendor/ui/js/test-libs/jquery-1.9.1.min.js +5 -0
  21. data/vendor/ui/js/test/app-test.js +386 -0
  22. data/vendor/ui/{test → js/test/components}/abbrev-test.js +0 -0
  23. data/vendor/ui/js/test/components/commit-linker-test.js +41 -0
  24. data/vendor/ui/js/test/components/profile-menu-test.js +46 -0
  25. data/vendor/ui/{test → js/test/components}/ref-selector-test.js +4 -1
  26. data/vendor/ui/{test → js/test/components}/tree-history-test.js +0 -0
  27. data/vendor/ui/{test → js/test/components}/url-template-test.js +9 -0
  28. data/vendor/ui/todo.org +4 -0
  29. metadata +38 -30
  30. data/Readme.md +0 -97
  31. data/vendor/ui/js/components/abbrev.js +0 -17
  32. data/vendor/ui/js/components/tree-history.js +0 -79
  33. data/vendor/ui/js/components/url.js +0 -31
@@ -0,0 +1,345 @@
1
+ /*global gts, dome, cull, bane, when*/
2
+ this.gts = this.gts || {};
3
+
4
+ /**
5
+ * An "app" is a mechanism for configuring "features"/UI components that should
6
+ * launch on page load and possibly also at later times. A "feature" is simply a
7
+ * function that may depend on an element, some data from the network and/or
8
+ * local variables. The app defines an API for adding such features, and
9
+ * provides a simple mechanism for them to declaratively state their
10
+ * dependencies.
11
+ *
12
+ * The app also has a mechanism for processing errors, logging debug information
13
+ * and reloading the features if e.g. parts of the page has been
14
+ * modified/reloaded.
15
+ *
16
+ * Environment variables
17
+ *
18
+ * Environment variables can be set by anyone at any time. This is typically
19
+ * useful whenever a server-side HTML template needs to inject some data into
20
+ * the client-side scripts, e.g.:
21
+ *
22
+ * <script>appInstance.env("ip_addr", "192.168.0.1");</script>
23
+ *
24
+ * Features can list environment variables as dependencies.
25
+ *
26
+ * Data
27
+ *
28
+ * Some features may require access to data that is not immediately present in
29
+ * the page. The typical example is data fetched via XMLHttpRequest, but 'data'
30
+ * is by no means restricted to that.
31
+ *
32
+ * Data is registered with a name and a function. The function will only ever be
33
+ * called if a task depends on it via its name. The function may either return
34
+ * some data directly, or return a promise. If it throws an error, or the
35
+ * returned promise rejects, the error will be passed on to the app's failure
36
+ * listeners. When the returned promise resolves, the result is passed to any
37
+ * features waiting for the data. Data may also have dependencies (e.g. on
38
+ * certain variables or other, see "Feature" below).
39
+ *
40
+ * Feature
41
+ *
42
+ * A "feature" is a function to be called on page load, and possibly also at a
43
+ * later point (e.g. if parts of the page has been rebuilt). Features may
44
+ * depend on specific elements to be available, data and environment variables.
45
+ * A feature may also have additional data provided as input for when it is
46
+ * called (if dependencies are resolved).
47
+ *
48
+ * Events
49
+ *
50
+ * The app emits the following events:
51
+ *
52
+ * "loading" (name)
53
+ *
54
+ * When a feature's dependencies are satiesfied, it is scheduled for loading. At
55
+ * this point some of the feature's input may still be unresolved (if any of it
56
+ * is the result of asynchronous operations). At this point, the feature may
57
+ * still fail to load, if asynchronous dependencies fail to materialize.
58
+ *
59
+ * "loaded" (name[, args...])
60
+ *
61
+ * When a feature has successfully materialized (i.e. the returned promise
62
+ * resolved, or it didn't return a promise). If a feature depends on multiple
63
+ * elements, this event will be emitted once per element.
64
+ *
65
+ * "pending" (name)
66
+ *
67
+ * A feature's dependencies were not satiesfied, thus it was not loaded. To
68
+ * investigate why the feature did not load, use the app object:
69
+ *
70
+ * app.on("pending", function (name) {
71
+ * app.dependencies(name); // [{ name: "A", loaded: false }, ...]
72
+ * });
73
+ *
74
+ * "error" (error)
75
+ *
76
+ * When errors occur, or promises are rejected as the app is loading features.
77
+ */
78
+ this.gts.app = function () {
79
+ var C = cull;
80
+
81
+ /**
82
+ * Check if `feature` has all its dependencies satiesfied in the `features`
83
+ * object (which uses feature/dependency names as keys, feature descriptions
84
+ * as values).
85
+ */
86
+ function dependenciesSatiesfied(features, feature) {
87
+ return C.reduce(function (satiesfied, dep) {
88
+ return satiesfied && features[dep] && features[dep].loaded;
89
+ }, true, feature.depends || []);
90
+ }
91
+
92
+ /**
93
+ * Return an array of "results" (return-values and/or resolved values from
94
+ * returned promises) of the features listed in `dependencies`.
95
+ */
96
+ function dependencyResults(features, deps) {
97
+ return C.map(function (dep) { return features[dep].result; }, deps);
98
+ }
99
+
100
+ /**
101
+ * Mark the feature as loaded and load it when all arguments have
102
+ * materialized.
103
+ */
104
+ function loadFeature(features, feature, element) {
105
+ var args = dependencyResults(features, feature.depends || []);
106
+ feature.loaded = true;
107
+ var deferred = when.defer();
108
+ feature.result = deferred.promise;
109
+ when.all(args).then(function (materialized) {
110
+ var allArgs = (element ? [element] : []).concat(materialized);
111
+ deferred.resolve(feature.action.apply(null, allArgs));
112
+ });
113
+ }
114
+
115
+ /**
116
+ * Attempt to load a feature in a given context. If the feature depends on
117
+ * elements, it will not be load if the provided context does not contain any
118
+ * matching elements.
119
+ */
120
+ function tryFeatureInContext(features, feature, context) {
121
+ var load = C.partial(loadFeature, features, feature);
122
+ if (feature.elements) {
123
+ C.doall(load, dome.byClass(feature.elements, context));
124
+ } else {
125
+ load();
126
+ }
127
+ }
128
+
129
+ /**
130
+ * When trying to load features, this function is used to determine if a
131
+ * feature is ready to be proactively loaded (and has not already been
132
+ * loaded).
133
+ */
134
+ function isReady(features, feature) {
135
+ return !feature.lazy &&
136
+ !feature.loaded &&
137
+ feature.action &&
138
+ dependenciesSatiesfied(features, feature);
139
+ }
140
+
141
+ /** Returns true if the feature is both pending (not loaded) and lazy */
142
+ function pendingLazy(feature) {
143
+ return feature && feature.lazy && !feature.loaded;
144
+ };
145
+
146
+ /**
147
+ * For all the features in `featureArr`, find the unique set of dependencies
148
+ * that are both pending and lazy.
149
+ */
150
+ function lazyDependencies(features, featureArr) {
151
+ var getDep = function (dep) { return features[dep]; };
152
+ return C.uniq(C.reduce(function (lazy, feature) {
153
+ return lazy.concat(C.select(
154
+ pendingLazy,
155
+ C.map(getDep, feature.depends || [])
156
+ ));
157
+ }, [], featureArr));
158
+ }
159
+
160
+ /** Set properties on all objects in the collection */
161
+ function setAll(objects, props) {
162
+ return C.doall(function (object) {
163
+ C.doall(function (prop) {
164
+ object[prop] = props[prop];
165
+ }, C.keys(props));
166
+ }, objects);
167
+ }
168
+
169
+ /** Temporarily mark a set of features as eager (i.e. not lazy) */
170
+ function makeEager(features) {
171
+ return setAll(features, { lazy: false, wasLazy: true });
172
+ }
173
+
174
+ /**
175
+ * Reset the state of features: Revert temporarily eager ones, and mark
176
+ * loaded features as not loaded so they can be considered for loading again
177
+ * (used for consecutive calls to load()).
178
+ */
179
+ function reset(features) {
180
+ C.doall(function (feature) {
181
+ // Environment variables don't have actions, they're always loaded
182
+ if (feature.action) { feature.loaded = false; }
183
+ if (feature.wasLazy) {
184
+ delete feature.wasLazy;
185
+ feature.lazy = true;
186
+ }
187
+ }, C.values(features));
188
+ }
189
+
190
+ /**
191
+ * Keep trying to load features until there are no more features ready to
192
+ * load. When one feature is enabled we start from the top again as that
193
+ * may have enabled features that were previously not ready.
194
+ */
195
+ function tryFeatures(app, features, context) {
196
+ var idx, feature, featureArr = C.values(features);
197
+ var deps = makeEager(lazyDependencies(features, featureArr));
198
+ var isReadyToLoad = C.partial(isReady, features);
199
+ var toTry = deps.concat(featureArr);
200
+
201
+ while ((feature = C.first(isReadyToLoad, toTry))) {
202
+ app.emit("loading", feature);
203
+ tryFeatureInContext(features, feature, context);
204
+ // If the feature is not loaded after trying, it's depending on
205
+ // elements, but no matching elements were found. Ignore this
206
+ // feature for now, re-evaluate during the next pass.
207
+ if (!feature.loaded) {
208
+ idx = C.indexOf(feature, toTry);
209
+ toTry = toTry.slice(0, idx).concat(toTry.slice(idx + 1));
210
+ }
211
+ }
212
+ }
213
+
214
+ function ensureUnique(features, name) {
215
+ if (features[name]) {
216
+ throw new Error("Cannot add duplicate " + name);
217
+ }
218
+ }
219
+
220
+ var appInstance;
221
+
222
+ function getDependencies() {
223
+ return cull.map(function (depName) {
224
+ return appInstance.features[depName] || {
225
+ name: depName,
226
+ type: "Unknown"
227
+ };
228
+ }, this.depends || []);
229
+ }
230
+
231
+ appInstance = bane.createEventEmitter({
232
+ features: {},
233
+
234
+ /**
235
+ * Set environment data. Values are not specially treated and can be
236
+ * anything. If the app has been loaded, setting an environment variable
237
+ * will result in trying to load pending features.
238
+ */
239
+ env: function (name, value) {
240
+ // ensureUnique(this.features, name);
241
+ this.env[name] = value;
242
+ this.feature(name, undefined, { result: value, loaded: true });
243
+ // this.features[name] = createFeature({ result: value, loaded: true });
244
+ // this.tryPending();
245
+ },
246
+
247
+ /**
248
+ * The data function may return a promise. If there are no tasks that
249
+ * depend on this piece of data, the function will never be called. It
250
+ * is possible to express dependencies for data - see lazy features
251
+ * below.
252
+ */
253
+ data: function (name, fn, opt) {
254
+ var options = opt || {};
255
+ if (typeof options.lazy !== "boolean") {
256
+ options.lazy = true;
257
+ }
258
+ return this.feature(name, fn, options);
259
+ },
260
+
261
+ /**
262
+ * Register a feature. Features may depend on environment variables,
263
+ * data, and even other features. Additionally, features may depend on
264
+ * DOM elements. DOM elements can only be selected by a single class
265
+ * name. If the class name matches no elements, the feature will not be
266
+ * called. Otherwise, the feature is called once for each element, like
267
+ * so:
268
+ *
269
+ * feature(element[, dependencies][, options]);
270
+ *
271
+ * Given the following feature:
272
+ *
273
+ * appInstance.feature("tweetui", loadTweets, {
274
+ * elements: "tweet-placeholder",
275
+ * depends: ["account", "tweets"]
276
+ * });
277
+ *
278
+ * Where "account" is an environment variable and "tweets" is a data
279
+ * event, the function will eventually be called like this:
280
+ *
281
+ * loadTweets(element1, accountValue, tweetsData);
282
+ * loadTweets(element2, accountValue, tweetsData);
283
+ * // ...
284
+ *
285
+ * If depending on another feature, its return-value will be the input.
286
+ * If the feature in question returned a promise, the resolution will be
287
+ * passed as input (after that feature has resolved).
288
+ *
289
+ * A feature may be "lazy", in which case it is only loaded if another
290
+ * feature depends on it. Data events are just lazy features, e.g.:
291
+ *
292
+ * appInstance.feature("tweets", function () {
293
+ * return reqwest({ url: "/tweets" });
294
+ * }, { lazy: true });
295
+ *
296
+ * Is equivalent to:
297
+ *
298
+ * appInstance.data("tweets", function () {
299
+ * return reqwest({ url: "/tweets" });
300
+ * });
301
+ *
302
+ * The `name` can be any string.
303
+ */
304
+ feature: function (name, fn, opt) {
305
+ ensureUnique(this.features, name);
306
+ var feature = opt || {};
307
+ feature.name = name;
308
+ feature.action = fn;
309
+ this.features[name] = feature;
310
+ this.features[name].dependencies = getDependencies;
311
+ this.tryPending();
312
+ },
313
+
314
+ /**
315
+ * Load the app. This function may be called multiple times. It
316
+ * optionally accepts a DOM element to use as its root. If it is not
317
+ * provided, the document itself is used as the root.
318
+ */
319
+ load: function (context) {
320
+ if (this.loaded) { reset(this.features); }
321
+ this.loaded = true;
322
+ this.context = context;
323
+ this.tryPending();
324
+ },
325
+
326
+ /**
327
+ * After loading the app, some features may still not be loaded if the
328
+ * elements they depend on are not available. `tryPending` retries all
329
+ * those features. If you call app.load(context) and then modify the DOM
330
+ * within the context element you may want to call this to ensure your
331
+ * modified elements are considered for pending features.
332
+ *
333
+ * If you want already loaded features to reload for newly added
334
+ * elements/changed DOM structure you need to call load() over again.
335
+ *
336
+ * If app is not loaded, this method does nothing.
337
+ */
338
+ tryPending: function () {
339
+ if (!this.loaded) { return; }
340
+ tryFeatures(this, this.features, this.context);
341
+ }
342
+ });
343
+
344
+ return appInstance;
345
+ };
@@ -0,0 +1,25 @@
1
+ /*global cull*/
2
+ // The global, shared Gitorious namespace
3
+ var gts = this.gts || {};
4
+
5
+ /**
6
+ * Abbreviates a string to fit within a predefined width. If the
7
+ * string is shorter than, or just as long as the specified width, it
8
+ * is returned untouched.
9
+ *
10
+ * If the optional suffix is provided, it is appended to the
11
+ * abbreviated string, and the full length of the abbreviated string
12
+ * with the suffix is guaranteed to not be wider than the specified
13
+ * width.
14
+ */
15
+ gts.abbrev = function (sentence, width, suffix) {
16
+ if (sentence.length <= width) { return sentence; }
17
+ suffix = suffix || "";
18
+
19
+ return cull.reduce(function (words, word) {
20
+ if (words.join(" ").length + word.length + suffix.length <= width) {
21
+ words.push(word);
22
+ }
23
+ return words;
24
+ }, [], sentence.split(" ")).join(" ") + suffix;
25
+ };
@@ -0,0 +1,26 @@
1
+ /*global cull, dome*/
2
+ // The global, shared Gitorious namespace
3
+ var gts = this.gts || {};
4
+
5
+ /**
6
+ * Adds a live event handler to a root element that fires on any
7
+ * element with the class "gts-commit-oid". This is used to link
8
+ * commit oids from Dolt to commit pages in Gitorious.
9
+ *
10
+ * root - The root element where "gts-commit-oid" links are expected
11
+ * urlTemplate - The URL template to link to. Should be a URL with the
12
+ * #{oid} placeholder.
13
+ * handler - The function to call when a commit link is clicked. The
14
+ * handler will be called with the resolved URL as its only
15
+ * argument.
16
+ */
17
+ (function (partial, d) {
18
+ gts.commitLinker = function (root, urlTemplate, handler) {
19
+ d.cn.add("gts-commit-linker", root);
20
+ d.delegate.bycn("gts-commit-oid", root, "click", function (e) {
21
+ handler(gts.url.render(urlTemplate, {
22
+ oid: dome.data.get("gts-commit-oid", this)
23
+ }));
24
+ });
25
+ };
26
+ }(cull.partial, dome));
@@ -0,0 +1,34 @@
1
+ /*global dome*/
2
+ // The global, shared Gitorious namespace
3
+ var gts = this.gts || {};
4
+
5
+ /**
6
+ * Builds the profile menu for the specified user and replaces the
7
+ * login button. If there is no user, nothing happens.
8
+ */
9
+ (function (d, e) {
10
+ function button(text, path, icon) {
11
+ var content = icon ? [e.i({className: "icon-" + icon}), text] : [text];
12
+ return e.li(e.a({ href: path }, content));
13
+ }
14
+
15
+ gts.profileMenu = function (root, user) {
16
+ if (!user) { return; }
17
+ d.setContent([
18
+ e.a({ className: "btn btn-inverse", href: user.dashboardPath },
19
+ e.i({ className: "icon-user icon-white" }, user.login)),
20
+ e.a({ className: "btn btn-inverse dropdown-toggle",
21
+ href: "#",
22
+ data: { toggle: "dropdown" }
23
+ }, e.span({ className: "caret" }, " ")),
24
+ e.ul({ className: "dropdown-menu" }, [
25
+ button("Edit", user.editPath, "pencil"),
26
+ button("Messages", user.messagesPath, "envelope"),
27
+ e.li({ className: "divider" }),
28
+ button("Dashboard", user.dashboardPath),
29
+ button("Public profile", user.profilePath),
30
+ button("Log out", user.logoutPath)
31
+ ])
32
+ ], root);
33
+ };
34
+ }(dome, dome.el));
@@ -1,13 +1,41 @@
1
1
  /*global cull*/
2
+ // The global, shared Gitorious namespace
2
3
  this.gts = this.gts || {};
3
4
 
4
- this.gts.refSelector = (function () {
5
- var e = cull.dom.el;
6
-
5
+ /**
6
+ * The ref selector builds an interactive "drop-down" menu from which the
7
+ * user can select a head or tag, or lookup a specific ref. The menu will
8
+ * reload the current page for the chosen ref.
9
+ *
10
+ * gts.refSelector(refs, current, urlTemplate)
11
+ *
12
+ * refs is an object containing available heads and tags and the oids they
13
+ * refer to, e.g.:
14
+ *
15
+ * { "heads": [["production", "24e9c0d4ce36fbe1dfca4029e3bd206d64e2eecc"],
16
+ * ["redesign", "4c22773401aa2cdd2391bc04443ad7eea193e7b6"],
17
+ * ["web-hooks", "a08053012b9aee5d9733fceb2cf3083f29d9aa7d"],
18
+ * ["master", "48ac677757da7ca052c59ebec0ded6e11eef2642"]],
19
+ * "tags": [["v2.4.3", "7a3cffcb3c3db89e8005962850e29a8aab2ab09b"]]
20
+ *
21
+ * current is the active ref on the current page, either the refname
22
+ * or the actual oid.
23
+ *
24
+ * urlTemplate is the URL that will be loaded, where the ref exists as a
25
+ * placeholder, e.g. "/gitorious/mainline/source/#{ref}:lib". A suitable
26
+ * template can be created from the current page's URL using
27
+ * gts.url.templatize(window.location.href, { ref: currentRef });
28
+ */
29
+ this.gts.refSelector = (function (e) {
7
30
  function tpl(template, ref) {
8
31
  return (template || "#{ref}").replace(/#\{ref\}/g, ref);
9
32
  }
10
33
 
34
+ /**
35
+ * Gets the current ref. type is {heads,tags,remotes} and
36
+ * refs[type] is an array of ref tuples where each tuple contains
37
+ * the object id and a ref (e.g. "master").
38
+ */
11
39
  function getCurrent(type, refName, refs) {
12
40
  return cull.select(function (ref) {
13
41
  return ref[0] === refName || ref[1] === refName;
@@ -73,4 +101,4 @@ this.gts.refSelector = (function () {
73
101
  className: "dropdown gts-branch-selector pull-right"
74
102
  }, [currentRefLink(refs, current), refsList(refs, urlTemplate)]);
75
103
  };
76
- }());
104
+ }(dome.el));