diff --git a/.gitmodules b/.gitmodules index 255017267..6794840bd 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "libgit2"] - path = libgit2 - url = git://github.com/pieter/libgit2.git + path = libgit2 + url = git://repo.or.cz/libgit2.git \ No newline at end of file diff --git a/BMDefines.h b/BMDefines.h new file mode 100644 index 000000000..02a874fab --- /dev/null +++ b/BMDefines.h @@ -0,0 +1,298 @@ +// +// BMDefines.h +// BMScriptTest +// +// Created by Andre Berg on 27.09.09. +// Copyright 2009 Berg Media. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/*! + * @file BMDefines.h + * + * Provides defines mostly for debugging. + * This file may be included by any project as it does not really contain project-specific symbols. + * Consists mainly of stuff I have gathered from multiple sources or defined in my own work to help + * ease debugging and/or cross-platform development. + */ + +#import +#import +#import +#include + +/// @cond HIDDEN +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _BM_DEFINES_H_ +#define _BM_DEFINES_H_ 1 +/// @endcond + + +/*! + * @addtogroup project_defines Project Defines + * @{ + */ + +// Determine runtine environment +#if !defined(__MACOSX_RUNTIME__) && !defined(__GNUSTEP_RUNTIME__) + #if defined(__APPLE__) && defined(__MACH__) && !defined(GNUSTEP) + /*! + * @def MACOSX_RUNTIME + * Determine runtime environment. + * Defined if running on Mac OS X. GNUStep will not have this defined. + */ + #define __MACOSX_RUNTIME__ + #endif // If not Mac OS X, GNUstep? + #if defined(GNUSTEP) && !defined(__MACOSX_RUNTIME__) + /*! + * @def __GNUSTEP_RUNTIME__ + * Determine runtime environment. + * Defined if running GNUStep. Mac OS X will not have this defined. + */ + #define __GNUSTEP_RUNTIME__ + #endif // Not Mac OS X or GNUstep, that's a problem. +#endif // !defined(__MACOSX_RUNTIME__) && !defined(__GNUSTEP_RUNTIME__) + +// If the above did not set the run time environment, error out. +#if !defined(__MACOSX_RUNTIME__) && !defined(__GNUSTEP_RUNTIME__) + #error Unable to determine run time environment, automatic Mac OS X and GNUstep detection failed +#endif + +/*! + * @def ENABLE_MACOSX_GARBAGE_COLLECTION + * Preprocessor definition to enable Mac OS X 10.5 (Leopard) Garbage Collection. + * This preprocessor define enables support for Garbage Collection on Mac OS X 10.5 (Leopard). + * + * Traditional retain / release functionality remains allowing the framework to be used in either + * Garbage Collected enabled applications or reference counting applications. + * + * The framework dynamically picks which mode to use at run-time base on whether or not the + * Garbage Collection system is active. + * + * @sa Garbage Collection Programming Guide + * @sa NSGarbageCollector Class Reference + */ +#if defined(__MACOSX_RUNTIME__) && defined(MAC_OS_X_VERSION_10_5) && defined(__OBJC_GC__) + #define ENABLE_MACOSX_GARBAGE_COLLECTION + #define BM_STRONG_REF __strong + #define BM_WEAK_REF __weak +#else + #define BM_STRONG_REF + #define BM_WEAK_REF +#endif + +#if defined(ENABLE_MACOSX_GARBAGE_COLLECTION) && !defined(MAC_OS_X_VERSION_10_5) +#error The Mac OS X Garbage Collection feature requires at least Mac OS X 10.5 +#endif + + +/*! + * A note to Clang's static analyzer. + * It tells about the returning onwnership intentions of methods. + * The header documentation of Apple follows: + * "Marks methods and functions which return an object that needs to be released by the caller but whose names are not consistent with Cocoa naming rules. The recommended fix to this is the rename the methods or functions, but this macro can be used to let the clang static analyzer know of any exceptions that cannot be fixed." + * + */ +#ifndef NS_RETURNS_RETAINED + #if defined(__clang__) + #define NS_RETURNS_RETAINED __attribute__((ns_returns_retained)) + #else + #define NS_RETURNS_RETAINED + #endif +#endif + + +// To simplify support for 64bit (and Leopard in general), +// provide the type defines for non Leopard SDKs +#if !(MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5) + + // NSInteger/NSUInteger and Max/Mins + #ifndef NSINTEGER_DEFINED + #if __LP64__ || NS_BUILD_32_LIKE_64 + typedef long NSInteger; + typedef unsigned long NSUInteger; + #else + typedef int NSInteger; + typedef unsigned int NSUInteger; + #endif + #define NSIntegerMax LONG_MAX + #define NSIntegerMin LONG_MIN + #define NSUIntegerMax ULONG_MAX + #define NSINTEGER_DEFINED 1 + #endif // NSINTEGER_DEFINED + + // CGFloat + #ifndef CGFLOAT_DEFINED + #if defined(__LP64__) && __LP64__ + // This really is an untested path (64bit on Tiger?) + typedef double CGFloat; + #define CGFLOAT_MIN DBL_MIN + #define CGFLOAT_MAX DBL_MAX + #define CGFLOAT_IS_DOUBLE 1 + #else /* !defined(__LP64__) || !__LP64__ */ + typedef float CGFloat; + #define CGFLOAT_MIN FLT_MIN + #define CGFLOAT_MAX FLT_MAX + #define CGFLOAT_IS_DOUBLE 0 + #endif /* !defined(__LP64__) || !__LP64__ */ + #define CGFLOAT_DEFINED 1 + #endif // CGFLOAT_DEFINED + + // NS_INLINE + #if !defined(NS_INLINE) + #if defined(__GNUC__) + #define NS_INLINE static __inline__ __attribute__((always_inline)) + #elif defined(__MWERKS__) || defined(__cplusplus) + #define NS_INLINE static inline + #elif defined(_MSC_VER) + #define NS_INLINE static __inline + #elif defined(__WIN32__) + #define NS_INLINE static __inline__ + #endif + #endif + +#endif // MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 + +/*! + * @def BM_C99(keyword) + * C99 conformance defines. + * Make it possible to safely use keywords and features of the C99 standard. + * @param keyword a C99 keyword (e.g. restrict) + */ +#if __STDC_VERSION__ >= 199901L + #define BM_C99(keyword) keyword +#else + #define BM_C99(keyword) +#endif + +/*! + * @def BM_REQUIRES_NIL_TERMINATION + * Used to mark variadic methods and functions as requiring nil termination. + * Nil termination means the last argument of their variable argument list must be nil. + */ +#if !defined(BM_REQUIRES_NIL_TERMINATION) + #if TARGET_OS_WIN32 + #define BM_REQUIRES_NIL_TERMINATION + #else + #if defined(__APPLE_CC__) && (__APPLE_CC__ >= 5549) + #define BM_REQUIRES_NIL_TERMINATION __attribute__((sentinel(0,1))) + #else + #define BM_REQUIRES_NIL_TERMINATION __attribute__((sentinel)) + #endif + #endif +#endif + +/*! + * @def BM_EXTERN + * Defines for the extern keyword. + * Makes it possible to use extern in the proper sense in C++ context included. + */ +/*! + * @def BM_PRIVATE_EXTERN + * Defines for the __private_extern__ Apple compiler directive. + * Makes a symbol public in the binrary (e.g. a library), but hidden outside of it. + */ +#ifdef __cplusplus + #define BM_EXTERN extern "C" + #define BM_PRIVATE_EXTERN __private_extern__ +#else + #define BM_EXTERN extern + #define BM_PRIVATE_EXTERN __private_extern__ +#endif + +/*! + * @def BM_ATTRIBUTES + * Macro wrapper around GCC __attribute__ syntax. + * @note When a compiler other than GCC 4+ is used, #BM_ATTRIBUTES evaluates to an empty string, removing itself and its arguments from the code to be compiled.

+ */ +/*! + * @def BM_EXPECTED + * Macro wrapper around GCC __builtin_expect syntax. + * + * From GCC docs: "You may use __builtin_expect to provide the compiler with branch prediction information. In general, you should prefer to use actual profile feedback for this (-fprofile-arcs), as programmers are notoriously bad at predicting how their programs actually perform. However, there are applications in which this data is hard to collect. + * + * The return value is the value of exp, which should be an integral expression. The value of c must be a compile-time constant. The semantics of the built-in are that it is expected that exp == c." + * + * And from RegexKit Framework docs (the origin of this macro): + * + *
Important:
BM_EXPECTED should only be used when the likelihood of the prediction is nearly certain. DO NOT GUESS.
+ * + * BM_EXPECTED [...] is used to provide the compiler with branch prediction information for conditional statements. + * + * An example of an appropriate use is parameter validation checks at the start of a function, such as (aPtr == NULL). Since callers are always expected to pass a valid pointer, the likelihood of the conditional evaluating to true is extremely unlikely. This allows the compiler to schedule instructions to minimize branch miss-prediction penalties. For example: +
if(BM_EXPECTED((aPtr == NULL), 0)) { abort(); }
+ * + * @note If a compiler other than GCC 4+ is used then the macro leaves the conditional expression unaltered. + */ +#if defined (__GNUC__) && (__GNUC__ >= 4) + #define BM_STATIC_INLINE static __inline__ __attribute__((always_inline)) + #define BM_STATIC_PURE_INLINE static __inline__ __attribute__((always_inline, pure)) + #define BM_EXPECTED(cond, expect) __builtin_expect(cond, expect) + #define BM_ALIGNED(boundary) __attribute__ ((aligned(boundary))) + #define BM_ATTRIBUTES(attr, ...) __attribute__((attr, ##__VA_ARGS__)) +#else + #define BM_STATIC_INLINE static __inline__ + #define BM_STATIC_PURE_INLINE static __inline__ + #define BM_EXPECTED(cond, expect) cond + #define BM_ALIGNED(boundary) + #define BM_ATTRIBUTES(attr, ...) +#endif + + +/*! + * @def BM_DEBUG_RETAIN_INIT + * Defines a macro which supplies replacement methods for -[retain] and -[release]. + * This macro is normally used in a global context (e.g. outside main) and followed by BM_DEBUG_RETAIN_SWIZZLE(className) in a local context, which then actually registers the replacement for the Class 'className' with the runtime. + * @attention This is only intended for debugging purposes. Has no effect if Garbage Collection is enabled. + */ +#define BM_DEBUG_RETAIN_INIT \ + IMP oldRetain;\ + IMP oldRelease;\ + id newRetain(id self, SEL _cmd) {\ + NSUInteger rc = [self retainCount];\ + NSLog(@"%s[0x%x]: retain, rc = %d -> %d",\ + class_getName([self class]), self, rc, rc + 1);\ + return (*oldRetain)(self, _cmd);\ + }\ + void newRelease(id self, SEL _cmd) {\ + NSUInteger rc = [self retainCount];\ + NSLog(@"%s[0x%x]: retain, rc = %d -> %d", \ + class_getName([self class]), self, rc, rc - 1);\ + (*oldRetain)(self, _cmd);\ + } + +/*! + * @def BM_DEBUG_RETAIN_SWIZZLE(className) + * Swizzles (or replaces) the methods defined by #BM_DEBUG_RETAIN_INIT for className. + * This macro is normally used in a (function) local scope, provided a #BM_DEBUG_RETAIN_INIT declaration at the beginning of the file (in global context). BM_DEBUG_RETAIN_SWIZZLE(className) then actually registers the replacements defined by #BM_DEBUG_RETAIN_INIT for the Class 'className' with the runtime. + * @attention This is only intended for debugging purposes. Has no effect if Garbage Collection is enabled. + * @param className the name of the class to replace the methods for (e.g. [SomeClass class]). + */ +#define BM_DEBUG_RETAIN_SWIZZLE(className) \ + oldRetain = class_getMethodImplementation((className), @selector(retain));\ + class_replaceMethod((className), @selector(retain), (IMP)&newRetain, "@@:");\ + oldRelease = class_getMethodImplementation((className), @selector(release));\ + class_replaceMethod((className), @selector(release), (IMP)&newRelease, "v@:"); + +/*! + * @} + */ + +#endif // _BM_DEFINES_H_ + +#ifdef __cplusplus +} /* extern "C" */ +#endif \ No newline at end of file diff --git a/BMScript.h b/BMScript.h new file mode 100644 index 000000000..9fa442852 --- /dev/null +++ b/BMScript.h @@ -0,0 +1,998 @@ +// +// BMScript.h +// BMScriptTest +// +// Created by Andre Berg on 11.09.09. +// Copyright 2009 Berg Media. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// MARK: Docs: Mainpage + +/*! + * @mainpage BMScript: Harness The Power Of Shell Scripts + *
+ * @par Introduction + * + * BMScript is an Objective-C class set to make it easier to utilize the + * power and flexibility of a whole range of scripting languages that already + * come with modern Macs. BMScript does not favor any particular scripting + * language or UNIXâ„¢ command line tool for that matter, instead it was written + * as an abstraction layer to NSTask, and as such supports any command line tool, + * provided that it is available on the target system. + * + * @par Usage + * + * BMScript can be used in two ways: + * + * -# Use it directly + * -# Guided by the BMScriptLanguageProtocol, make a subclass from it + * + * The easiest way to use BMScript is, of course, to instanciate it directly: + * + * @include bmScriptCreationMethods.m + * + * You typically use the designated initializer for which you supply the script + * source and script options yourself.
+ * The options dictionary then looks like this: + * + * @include bmScriptOptionsDictionary.m + * + * There's two constant keys. These are the only keys you need to define values for. + * #BMScriptOptionsTaskLaunchPathKey stores the path to the tool's executable and + * #BMScriptOptionsTaskArgumentsKey is a nil-terminated variable list of parameters + * to be used as arguments to the task which will load and execute the tool found at + * the launch path specified for the other key. + * + * It is very important to note that the script source string should NOT be + * supplied in the array for the #BMScriptOptionsTaskArgumentsKey, as it will be added + * later by the class after performing tests and delegation which could alter the script + * in ways needed to safely execute it. This is in the delegate object's responsibility. + * + * A macro function called #BMSynthesizeOptions(path, args) is available to ease + * the declaration of the options.
+ * Here is the definition: + * + * @include bmScriptSynthesizeOptions.m + * + *
+ *
+ *
+ *
Important:
+ *
+ * Don't forget the nil at the end even + * if you don't need to supply any task arguments. + *
+ *
+ *
+ *
+ * + * If you initialize BMScript directly without specifying options and script source + * (e.g. using [[%BMScript alloc] init]) the options + * will default to BMSynthesizeOptions(@"/bin/echo", @"") + * and the script source will default to @"' tags. */ + scriptScriptTags : { left: /(<|<)\s*script.*?(>|>)/gi, right: /(<|<)\/\s*script\s*(>|>)/gi } + }, + + toolbar : { + /** + * Creates new toolbar for a highlighter. + * @param {Highlighter} highlighter Target highlighter. + */ + create : function(highlighter) + { + var div = document.createElement('DIV'), + items = sh.toolbar.items + ; + + div.className = 'toolbar'; + + for (var name in items) + { + var constructor = items[name], + command = new constructor(highlighter), + element = command.create() + ; + + highlighter.toolbarCommands[name] = command; + + if (element == null) + continue; + + if (typeof(element) == 'string') + element = sh.toolbar.createButton(element, highlighter.id, name); + + element.className += 'item ' + name; + div.appendChild(element); + } + + return div; + }, + + /** + * Create a standard anchor button for the toolbar. + * @param {String} label Label text to display. + * @param {String} highlighterId Highlighter ID that this button would belong to. + * @param {String} commandName Command name that would be executed. + * @return {Element} Returns an 'A' element. + */ + createButton : function(label, highlighterId, commandName) + { + var a = document.createElement('a'), + style = a.style, + config = sh.config, + width = config.toolbarItemWidth, + height = config.toolbarItemHeight + ; + + a.href = '#' + commandName; + a.title = label; + a.highlighterId = highlighterId; + a.commandName = commandName; + a.innerHTML = label; + + if (isNaN(width) == false) + style.width = width + 'px'; + + if (isNaN(height) == false) + style.height = height + 'px'; + + a.onclick = function(e) + { + try + { + sh.toolbar.executeCommand( + this, + e || window.event, + this.highlighterId, + this.commandName + ); + } + catch(e) + { + sh.utils.alert(e.message); + } + + return false; + }; + + return a; + }, + + /** + * Executes a toolbar command. + * @param {Element} sender Sender element. + * @param {MouseEvent} event Original mouse event object. + * @param {String} highlighterId Highlighter DIV element ID. + * @param {String} commandName Name of the command to execute. + * @return {Object} Passes out return value from command execution. + */ + executeCommand : function(sender, event, highlighterId, commandName, args) + { + var highlighter = sh.vars.highlighters[highlighterId], + command + ; + + if (highlighter == null || (command = highlighter.toolbarCommands[commandName]) == null) + return null; + + return command.execute(sender, event, args); + }, + + /** Collection of toolbar items. */ + items : { + expandSource : function(highlighter) + { + this.create = function() + { + if (highlighter.getParam('collapse') != true) + return; + + return sh.config.strings.expandSource; + }; + + this.execute = function(sender, event, args) + { + var div = highlighter.div; + + sender.parentNode.removeChild(sender); + div.className = div.className.replace('collapsed', ''); + }; + }, + + /** + * Command to open a new window and display the original unformatted source code inside. + */ + viewSource : function(highlighter) + { + this.create = function() + { + return sh.config.strings.viewSource; + }; + + this.execute = function(sender, event, args) + { + var code = sh.utils.fixInputString(highlighter.originalCode).replace(/' + code + ''); + wnd.document.close(); + }; + }, + + /** + * Command to copy the original source code in to the clipboard. + * Uses Flash method if clipboardSwf is configured. + */ + copyToClipboard : function(highlighter) + { + var flashDiv, flashSwf, + highlighterId = highlighter.id + ; + + this.create = function() + { + var config = sh.config; + + // disable functionality if running locally + if (config.clipboardSwf == null) + return null; + + function params(list) + { + var result = ''; + + for (var name in list) + result += ""; + + return result; + }; + + function attributes(list) + { + var result = ''; + + for (var name in list) + result += " " + name + "='" + list[name] + "'"; + + return result; + }; + + var args1 = { + width : config.toolbarItemWidth, + height : config.toolbarItemHeight, + id : highlighterId + '_clipboard', + type : 'application/x-shockwave-flash', + title : sh.config.strings.copyToClipboard + }, + + // these arguments are used in IE's collection + args2 = { + allowScriptAccess : 'always', + wmode : 'transparent', + flashVars : 'highlighterId=' + highlighterId, + menu : 'false' + }, + swf = config.clipboardSwf, + html + ; + + if (/msie/i.test(navigator.userAgent)) + { + html = '' + + params(args2) + + params({ movie : swf }) + + '' + ; + } + else + { + html = '' + ; + } + + flashDiv = document.createElement('div'); + flashDiv.innerHTML = html; + + return flashDiv; + }; + + this.execute = function(sender, event, args) + { + var command = args.command; + + switch (command) + { + case 'get': + var code = sh.utils.unindent( + sh.utils.fixInputString(highlighter.originalCode) + .replace(/</g, '<') + .replace(/>/g, '>') + .replace(/&/g, '&') + ); + + if(window.clipboardData) + // will fall through to the confirmation because there isn't a break + window.clipboardData.setData('text', code); + else + return sh.utils.unindent(code); + + case 'ok': + sh.utils.alert(sh.config.strings.copyToClipboardConfirmation); + break; + + case 'error': + sh.utils.alert(args.message); + break; + } + }; + }, + + /** Command to print the colored source code. */ + printSource : function(highlighter) + { + this.create = function() + { + return sh.config.strings.print; + }; + + this.execute = function(sender, event, args) + { + var iframe = document.createElement('IFRAME'), + doc = null + ; + + // make sure there is never more than one hidden iframe created by SH + if (sh.vars.printFrame != null) + document.body.removeChild(sh.vars.printFrame); + + sh.vars.printFrame = iframe; + + // this hides the iframe + iframe.style.cssText = 'position:absolute;width:0px;height:0px;left:-500px;top:-500px;'; + + document.body.appendChild(iframe); + doc = iframe.contentWindow.document; + + copyStyles(doc, window.document); + doc.write('
' + highlighter.div.innerHTML + '
'); + doc.close(); + + iframe.contentWindow.focus(); + iframe.contentWindow.print(); + + function copyStyles(destDoc, sourceDoc) + { + var links = sourceDoc.getElementsByTagName('link'); + + for(var i = 0; i < links.length; i++) + if(links[i].rel.toLowerCase() == 'stylesheet' && /shCore\.css$/.test(links[i].href)) + destDoc.write(''); + }; + }; + }, + + /** Command to display the about dialog window. */ + about : function(highlighter) + { + this.create = function() + { + return sh.config.strings.help; + }; + + this.execute = function(sender, event) + { + var wnd = sh.utils.popup('', '_blank', 500, 250, 'scrollbars=0'), + doc = wnd.document + ; + + doc.write(sh.config.strings.aboutDialog); + doc.close(); + wnd.focus(); + }; + } + } + }, + + utils : { + /** + * Finds an index of element in the array. + * @ignore + * @param {Object} searchElement + * @param {Number} fromIndex + * @return {Number} Returns index of element if found; -1 otherwise. + */ + indexOf : function(array, searchElement, fromIndex) + { + fromIndex = Math.max(fromIndex || 0, 0); + + for (var i = fromIndex; i < array.length; i++) + if(array[i] == searchElement) + return i; + + return -1; + }, + + /** + * Generates a unique element ID. + */ + guid : function(prefix) + { + return prefix + Math.round(Math.random() * 1000000).toString(); + }, + + /** + * Merges two objects. Values from obj2 override values in obj1. + * Function is NOT recursive and works only for one dimensional objects. + * @param {Object} obj1 First object. + * @param {Object} obj2 Second object. + * @return {Object} Returns combination of both objects. + */ + merge: function(obj1, obj2) + { + var result = {}, name; + + for (name in obj1) + result[name] = obj1[name]; + + for (name in obj2) + result[name] = obj2[name]; + + return result; + }, + + /** + * Attempts to convert string to boolean. + * @param {String} value Input string. + * @return {Boolean} Returns true if input was "true", false if input was "false" and value otherwise. + */ + toBoolean: function(value) + { + switch (value) + { + case "true": + return true; + + case "false": + return false; + } + + return value; + }, + + /** + * Opens up a centered popup window. + * @param {String} url URL to open in the window. + * @param {String} name Popup name. + * @param {int} width Popup width. + * @param {int} height Popup height. + * @param {String} options window.open() options. + * @return {Window} Returns window instance. + */ + popup: function(url, name, width, height, options) + { + var x = (screen.width - width) / 2, + y = (screen.height - height) / 2 + ; + + options += ', left=' + x + + ', top=' + y + + ', width=' + width + + ', height=' + height + ; + options = options.replace(/^,/, ''); + + var win = window.open(url, name, options); + win.focus(); + return win; + }, + + /** + * Adds event handler to the target object. + * @param {Object} obj Target object. + * @param {String} type Name of the event. + * @param {Function} func Handling function. + */ + addEvent: function(obj, type, func) + { + if (obj.attachEvent) + { + obj['e' + type + func] = func; + obj[type + func] = function() + { + obj['e' + type + func](window.event); + } + obj.attachEvent('on' + type, obj[type + func]); + } + else + { + obj.addEventListener(type, func, false); + } + }, + + /** + * Displays an alert. + * @param {String} str String to display. + */ + alert: function(str) + { + alert(sh.config.strings.alert + str) + }, + + /** + * Finds a brush by its alias. + * + * @param {String} alias Brush alias. + * @param {Boolean} alert Suppresses the alert if false. + * @return {Brush} Returns bursh constructor if found, null otherwise. + */ + findBrush: function(alias, alert) + { + var brushes = sh.vars.discoveredBrushes, + result = null + ; + + if (brushes == null) + { + brushes = {}; + + // Find all brushes + for (var brush in sh.brushes) + { + var aliases = sh.brushes[brush].aliases; + + if (aliases == null) + continue; + + // keep the brush name + sh.brushes[brush].name = brush.toLowerCase(); + + for (var i = 0; i < aliases.length; i++) + brushes[aliases[i]] = brush; + } + + sh.vars.discoveredBrushes = brushes; + } + + result = sh.brushes[brushes[alias]]; + + if (result == null && alert != false) + sh.utils.alert(sh.config.strings.noBrush + alias); + + return result; + }, + + /** + * Executes a callback on each line and replaces each line with result from the callback. + * @param {Object} str Input string. + * @param {Object} callback Callback function taking one string argument and returning a string. + */ + eachLine: function(str, callback) + { + var lines = str.split('\n'); + + for (var i = 0; i < lines.length; i++) + lines[i] = callback(lines[i]); + + return lines.join('\n'); + }, + + /** + * This is a special trim which only removes first and last empty lines + * and doesn't affect valid leading space on the first line. + * + * @param {String} str Input string + * @return {String} Returns string without empty first and last lines. + */ + trimFirstAndLastLines: function(str) + { + return str.replace(/^[ ]*[\n]+|[\n]*[ ]*$/g, ''); + }, + + /** + * Parses key/value pairs into hash object. + * + * Understands the following formats: + * - name: word; + * - name: [word, word]; + * - name: "string"; + * - name: 'string'; + * + * For example: + * name1: value; name2: [value, value]; name3: 'value' + * + * @param {String} str Input string. + * @return {Object} Returns deserialized object. + */ + parseParams: function(str) + { + var match, + result = {}, + arrayRegex = new XRegExp("^\\[(?(.*?))\\]$"), + regex = new XRegExp( + "(?[\\w-]+)" + + "\\s*:\\s*" + + "(?" + + "[\\w-%#]+|" + // word + "\\[.*?\\]|" + // [] array + '".*?"|' + // "" string + "'.*?'" + // '' string + ")\\s*;?", + "g" + ) + ; + + while ((match = regex.exec(str)) != null) + { + var value = match.value + .replace(/^['"]|['"]$/g, '') // strip quotes from end of strings + ; + + // try to parse array value + if (value != null && arrayRegex.test(value)) + { + var m = arrayRegex.exec(value); + value = m.values.length > 0 ? m.values.split(/\s*,\s*/) : []; + } + + result[match.name] = value; + } + + return result; + }, + + /** + * Wraps each line of the string into tag with given style applied to it. + * + * @param {String} str Input string. + * @param {String} css Style name to apply to the string. + * @return {String} Returns input string with each line surrounded by tag. + */ + decorate: function(str, css) + { + if (str == null || str.length == 0 || str == '\n') + return str; + + str = str.replace(/... to them so that + // leading spaces aren't included. + if (css != null) + str = sh.utils.eachLine(str, function(line) + { + if (line.length == 0) + return ''; + + var spaces = ''; + + line = line.replace(/^( | )+/, function(s) + { + spaces = s; + return ''; + }); + + if (line.length == 0) + return spaces; + + return spaces + '' + line + ''; + }); + + return str; + }, + + /** + * Pads number with zeros until it's length is the same as given length. + * + * @param {Number} number Number to pad. + * @param {Number} length Max string length with. + * @return {String} Returns a string padded with proper amount of '0'. + */ + padNumber : function(number, length) + { + var result = number.toString(); + + while (result.length < length) + result = '0' + result; + + return result; + }, + + /** + * Measures width of a single space character. + * @return {Number} Returns width of a single space character. + */ + measureSpace : function() + { + var container = document.createElement('div'), + span, + result = 0, + body = document.body, + id = sh.utils.guid('measureSpace'), + + // variable names will be compressed, so it's better than a plain string + divOpen = '
' + + divOpen + 'lines">' + + divOpen + 'line">' + + divOpen + 'content' + + '"> ' + closeSpan + closeSpan + + closeDiv + + closeDiv + + closeDiv + + closeDiv + ; + + body.appendChild(container); + span = document.getElementById(id); + + if (/opera/i.test(navigator.userAgent)) + { + var style = window.getComputedStyle(span, null); + result = parseInt(style.getPropertyValue("width")); + } + else + { + result = span.offsetWidth; + } + + body.removeChild(container); + + return result; + }, + + /** + * Replaces tabs with spaces. + * + * @param {String} code Source code. + * @param {Number} tabSize Size of the tab. + * @return {String} Returns code with all tabs replaces by spaces. + */ + processTabs : function(code, tabSize) + { + var tab = ''; + + for (var i = 0; i < tabSize; i++) + tab += ' '; + + return code.replace(/\t/g, tab); + }, + + /** + * Replaces tabs with smart spaces. + * + * @param {String} code Code to fix the tabs in. + * @param {Number} tabSize Number of spaces in a column. + * @return {String} Returns code with all tabs replaces with roper amount of spaces. + */ + processSmartTabs : function(code, tabSize) + { + var lines = code.split('\n'), + tab = '\t', + spaces = '' + ; + + // Create a string with 1000 spaces to copy spaces from... + // It's assumed that there would be no indentation longer than that. + for (var i = 0; i < 50; i++) + spaces += ' '; // 20 spaces * 50 + + // This function inserts specified amount of spaces in the string + // where a tab is while removing that given tab. + function insertSpaces(line, pos, count) + { + return line.substr(0, pos) + + spaces.substr(0, count) + + line.substr(pos + 1, line.length) // pos + 1 will get rid of the tab + ; + }; + + // Go through all the lines and do the 'smart tabs' magic. + code = sh.utils.eachLine(code, function(line) + { + if (line.indexOf(tab) == -1) + return line; + + var pos = 0; + + while ((pos = line.indexOf(tab)) != -1) + { + // This is pretty much all there is to the 'smart tabs' logic. + // Based on the position within the line and size of a tab, + // calculate the amount of spaces we need to insert. + var spaces = tabSize - pos % tabSize; + line = insertSpaces(line, pos, spaces); + } + + return line; + }); + + return code; + }, + + /** + * Performs various string fixes based on configuration. + */ + fixInputString : function(str) + { + var br = /|<br\s*\/?>/gi; + + if (sh.config.bloggerMode == true) + str = str.replace(br, '\n'); + + if (sh.config.stripBrs == true) + str = str.replace(br, ''); + + return str; + }, + + /** + * Removes all white space at the begining and end of a string. + * + * @param {String} str String to trim. + * @return {String} Returns string without leading and following white space characters. + */ + trim: function(str) + { + return str.replace(/^\s+|\s+$/g, ''); + }, + + /** + * Unindents a block of text by the lowest common indent amount. + * @param {String} str Text to unindent. + * @return {String} Returns unindented text block. + */ + unindent: function(str) + { + var lines = sh.utils.fixInputString(str).split('\n'), + indents = new Array(), + regex = /^\s*/, + min = 1000 + ; + + // go through every line and check for common number of indents + for (var i = 0; i < lines.length && min > 0; i++) + { + var line = lines[i]; + + if (sh.utils.trim(line).length == 0) + continue; + + var matches = regex.exec(line); + + // In the event that just one line doesn't have leading white space + // we can't unindent anything, so bail completely. + if (matches == null) + return str; + + min = Math.min(matches[0].length, min); + } + + // trim minimum common number of white space from the begining of every line + if (min > 0) + for (var i = 0; i < lines.length; i++) + lines[i] = lines[i].substr(min); + + return lines.join('\n'); + }, + + /** + * Callback method for Array.sort() which sorts matches by + * index position and then by length. + * + * @param {Match} m1 Left object. + * @param {Match} m2 Right object. + * @return {Number} Returns -1, 0 or -1 as a comparison result. + */ + matchesSortCallback: function(m1, m2) + { + // sort matches by index first + if(m1.index < m2.index) + return -1; + else if(m1.index > m2.index) + return 1; + else + { + // if index is the same, sort by length + if(m1.length < m2.length) + return -1; + else if(m1.length > m2.length) + return 1; + } + + return 0; + }, + + /** + * Executes given regular expression on provided code and returns all + * matches that are found. + * + * @param {String} code Code to execute regular expression on. + * @param {Object} regex Regular expression item info from regexList collection. + * @return {Array} Returns a list of Match objects. + */ + getMatches: function(code, regexInfo) + { + function defaultAdd(match, regexInfo) + { + return [new sh.Match(match[0], match.index, regexInfo.css)]; + }; + + var index = 0, + match = null, + result = [], + func = regexInfo.func ? regexInfo.func : defaultAdd + ; + + while((match = regexInfo.regex.exec(code)) != null) + result = result.concat(func(match, regexInfo)); + + return result; + }, + + processUrls: function(code) + { + var lt = '<', + gt = '>' + ; + + return code.replace(sh.regexLib.url, function(m) + { + var suffix = '', prefix = ''; + + // We include < and > in the URL for the common cases like + // The problem is that they get transformed into <http://google.com> + // Where as > easily looks like part of the URL string. + + if (m.indexOf(lt) == 0) + { + prefix = lt; + m = m.substring(lt.length); + } + + if (m.indexOf(gt) == m.length - gt.length) + { + m = m.substring(0, m.length - gt.length); + suffix = gt; + } + + return prefix + '' + m + '' + suffix; + }); + }, + + /** + * Finds all + + + + + + + + + + + + + + + + + + + + + + + +

SyntaxHihglighter Test

+

This is a test file to insure that everything is working well.

+ +
+function test() : String
+{
+	return 10;
+}
+
+ diff --git a/html/views/blame/blame.js b/html/views/blame/blame.js index a89326bc7..5bcbc627e 100644 --- a/html/views/blame/blame.js +++ b/html/views/blame/blame.js @@ -7,3 +7,7 @@ var showFile = function(txt) { SyntaxHighlighter.highlight(); return; } + +var selectCommit = function(a) { + Controller.selectCommit_(a); +} \ No newline at end of file diff --git a/html/views/blame/index copy.html b/html/views/blame/index copy.html new file mode 100644 index 000000000..af371b686 --- /dev/null +++ b/html/views/blame/index copy.html @@ -0,0 +1,726 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

author Pieter de Bie

summary Add a Tree displayer

+
    //
+    //  PBGitTree.m
+    //  GitTest
+    //
+    //  Created by Pieter de Bie on 15-06-08.
+    //  Copyright 2008 __MyCompanyName__. All rights reserved.
+    //
+    
+    #import "PBGitTree.h"
+    #import "PBGitCommit.h"
+    #import "NSFileHandleExt.h"
+

author Pieter de Bie

summary Allow double click to open file

+
    #import "PBEasyPipe.h"
+

author Pieter de Bie

summary Delete temporary files when they are deallocated

+
    #import "PBEasyFS.h"
+

author Pieter de Bie

summary Add a Tree displayer

+
    
+    @implementation PBGitTree
+    
+    @synthesize sha, path, repository, leaf, parent;
+    
+    + (PBGitTree*) rootForCommit:(id) commit
+    {
+

author Pieter de Bie

summary Add a Tree displayer

+
        PBGitCommit* c = commit;
+        PBGitTree* tree = [[self alloc] init];
+        tree.parent = nil;
+        tree.leaf = NO;
+

author Pieter de Bie

summary Include libgit2 as submodule and use it to store sha's

+
        tree.sha = [c realSha];
+

author Pieter de Bie

summary Add a Tree displayer

+
        tree.repository = c.repository;
+        tree.path = @"";
+        return tree;
+    }
+    
+    + (PBGitTree*) treeForTree: (PBGitTree*) prev andPath: (NSString*) path;
+    {
+        PBGitTree* tree = [[self alloc] init];
+        tree.parent = prev;
+        tree.sha = prev.sha;
+        tree.repository = prev.repository;
+        tree.path = path;
+        return tree;
+    }
+    
+    - init
+    {
+        children = nil;
+

author Pieter de Bie

summary Delete temporary files when they are deallocated

+
        localFileName = nil;
+

author Pieter de Bie

summary Add a Tree displayer

+
        leaf = YES;
+        return self;
+    }
+    
+

author Pieter de Bie

summary Use unified interface and display tree contents

+
    - (NSString*) refSpec
+    {
+        return [NSString stringWithFormat:@"%@:%@", self.sha, self.fullPath];
+    }
+    
+

author Pieter de Bie

summary Delete temporary files when they are deallocated

+
    - (BOOL) isLocallyCached
+    {
+        NSFileManager* fs = [NSFileManager defaultManager];
+        if (localFileName && [fs fileExistsAtPath:localFileName])
+        {
+            NSDate* mtime = [[fs attributesOfItemAtPath:localFileName error: nil] objectForKey:NSFileModificationDate];
+            if ([mtime compare:localMtime] == 0)
+                return YES;
+        }
+        return NO;
+    }
+    
+

author Johannes Gilger

summary PBGitTree: Improve binary-file decision

+
    - (BOOL)hasBinaryHeader:(NSString*)contents
+

author Johannes Gilger

summary PBGitTree: Don't try to print binary-file contents

+
    {
+

author Johannes Gilger

summary PBGitTree: Improve binary-file decision

+
        if(!contents)
+

author Johannes Gilger

summary PBGitTree: Don't try to print binary-file contents

+
            return NO;
+    
+

author Johannes Gilger

summary PBGitTree: Improve binary-file decision

+
        return [contents rangeOfString:@"\0" options:0 range:NSMakeRange(0, ([contents length] >= 8000) ? 7999 : [contents length])].location != NSNotFound;
+

author Johannes Gilger

summary PBGitTree: Don't try to print binary-file contents

+
    }
+    
+    - (BOOL)hasBinaryAttributes
+    {
+        // First ask git check-attr if the file has a binary attribute custom set
+        NSFileHandle *handle = [repository handleInWorkDirForArguments:[NSArray arrayWithObjects:@"check-attr", @"binary", [self fullPath], nil]];
+        NSData *data = [handle readDataToEndOfFile];
+        NSString *string = [[NSString alloc] initWithData:data encoding:NSISOLatin1StringEncoding];
+    
+        if (!string)
+            return NO;
+        string = [string stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]];
+    
+        if ([string hasSuffix:@"binary: set"])
+            return YES;
+    
+        if ([string hasSuffix:@"binary: unset"])
+            return NO;
+    
+        // Binary state unknown, do a check on common filename-extensions
+        for (NSString *extension in [NSArray arrayWithObjects:@".pdf", @".jpg", @".jpeg", @".png", @".bmp", @".gif", @".o", nil]) {
+            if ([[self fullPath] hasSuffix:extension])
+                return YES;
+        }
+    
+        return NO;
+    }
+    
+

author Pieter de Bie

summary Use unified interface and display tree contents

+
    - (NSString*) contents
+    {
+        if (!leaf)
+

author dbr

summary When selecting a folder in tree-view, display the

+
            return [NSString stringWithFormat:@"This is a tree with path %@", [self fullPath]];
+

author German Laullon

summary initial blame functionality on tree view

+
        
+

author Johannes Gilger

summary PBGitTree: Don't try to print binary-file contents

+
        if ([self isLocallyCached]) {
+            NSData *data = [NSData dataWithContentsOfFile:localFileName];
+            NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
+            if (!string)
+                string = [[NSString alloc] initWithData:data encoding:NSISOLatin1StringEncoding];
+            return string;
+

author Pieter de Bie

summary Delete temporary files when they are deallocated

+
        }
+        
+

author German Laullon

summary initial blame functionality on tree view

+
        //return [repository outputForArguments:[NSArray arrayWithObjects:@"show", [self refSpec], nil]];
+        return [repository outputInWorkdirForArguments:[NSArray arrayWithObjects:@"blame", self.path, nil]];
+    }
+    
+

author German Laullon

summary HTML Blame

+
    // XXX: create img tag for images.
+

author German Laullon

summary initial blame functionality on tree view

+
    - (NSString*) contents:(NSInteger)option
+    {
+

author German Laullon

summary HTML Blame

+
        NSString* contents;
+    
+

author German Laullon

summary initial blame functionality on tree view

+
        if (!leaf)
+            return [NSString stringWithFormat:@"This is a tree with path %@", [self fullPath]];
+        
+

author German Laullon

summary HTML Blame

+
        if ([self hasBinaryAttributes])
+            return [NSString stringWithFormat:@"%@ appears to be a binary file of %d bytes", [self fullPath], [self fileSize]];
+        
+        if ([self fileSize] > 52428800) // ~50MB
+            return [NSString stringWithFormat:@"%@ is too big to be displayed (%d bytes)", [self fullPath], [self fileSize]];
+        
+

author German Laullon

summary initial blame functionality on tree view

+
        if(option==0)
+

author German Laullon

summary HTML Blame

+
            contents= [repository outputForArguments:[NSArray arrayWithObjects:@"show", [self refSpec], nil]];
+

author German Laullon

summary initial blame functionality on tree view

+
        else
+

author German Laullon

summary HTML Blame

+
            contents=[PBGitTree parseBlame:[repository outputInWorkdirForArguments:[NSArray arrayWithObjects:@"blame", @"-p", self.fullPath, nil]]];
+        
+        if ([self hasBinaryHeader:contents])
+            return [NSString stringWithFormat:@"%@ appears to be a binary file of %d bytes", [self fullPath], [self fileSize]];
+        
+        return contents;
+

author Johannes Gilger

summary PBGitTree: Don't try to print binary-file contents

+
    }
+    
+    - (long long)fileSize
+    {
+        if (_fileSize)
+            return _fileSize;
+    
+        NSFileHandle *handle = [repository handleForArguments:[NSArray arrayWithObjects:@"cat-file", @"-s", [self refSpec], nil]];
+        NSString *sizeString = [[NSString alloc] initWithData:[handle readDataToEndOfFile] encoding:NSISOLatin1StringEncoding];
+    
+        if (!sizeString)
+            _fileSize = -1;
+        else
+            _fileSize = [sizeString longLongValue];
+    
+        return _fileSize;
+    }
+    
+    - (NSString *)textContents
+    {
+        if (!leaf)
+            return [NSString stringWithFormat:@"This is a tree with path %@", [self fullPath]];
+    
+        if ([self hasBinaryAttributes])
+            return [NSString stringWithFormat:@"%@ appears to be a binary file of %d bytes", [self fullPath], [self fileSize]];
+    
+

author Johannes Gilger

summary PBGitTree: Improve binary-file decision

+
        if ([self fileSize] > 52428800) // ~50MB
+            return [NSString stringWithFormat:@"%@ is too big to be displayed (%d bytes)", [self fullPath], [self fileSize]];
+

author Johannes Gilger

summary PBGitTree: Don't try to print binary-file contents

+
    
+

author Johannes Gilger

summary PBGitTree: Improve binary-file decision

+
        NSString* contents = [self contents];
+

author Johannes Gilger

summary PBGitTree: Don't try to print binary-file contents

+
    
+

author Johannes Gilger

summary PBGitTree: Improve binary-file decision

+
        if ([self hasBinaryHeader:contents])
+            return [NSString stringWithFormat:@"%@ appears to be a binary file of %d bytes", [self fullPath], [self fileSize]];
+

author Johannes Gilger

summary PBGitTree: Don't try to print binary-file contents

+
    
+        return contents;
+

author Pieter de Bie

summary Use unified interface and display tree contents

+
    }
+    
+

author Pieter de Bie

summary Delete temporary files when they are deallocated

+
    - (void) saveToFolder: (NSString *) dir
+    {
+        NSString* newName = [dir stringByAppendingPathComponent:path];
+    
+        if (leaf) {
+            NSFileHandle* handle = [repository handleForArguments:[NSArray arrayWithObjects:@"show", [self refSpec], nil]];
+            NSData* data = [handle readDataToEndOfFile];
+            [data writeToFile:newName atomically:YES];
+        } else { // Directory
+            [[NSFileManager defaultManager] createDirectoryAtPath:newName attributes:nil];
+

author Michael Stephens

summary Fix saving tree to folder

+
            for (PBGitTree* child in [self children])
+

author Pieter de Bie

summary Delete temporary files when they are deallocated

+
                [child saveToFolder: newName];
+        }
+    }
+    
+    - (NSString*) tmpDirWithContents
+    {
+        if (leaf)
+            return nil;
+    
+        if (!localFileName)
+            localFileName = [PBEasyFS tmpDirWithPrefix: path];
+    
+

author Pieter de Bie

summary Delete temporary files when they are deallocated

+
        for (PBGitTree* child in [self children]) {
+

author Pieter de Bie

summary Delete temporary files when they are deallocated

+
            [child saveToFolder: localFileName];
+        }
+        
+        return localFileName;
+    }
+    
+        
+    
+

author Pieter de Bie

summary Allow double click to open file

+
    - (NSString*) tmpFileNameForContents
+    {
+        if (!leaf)
+

author Pieter de Bie

summary Delete temporary files when they are deallocated

+
            return [self tmpDirWithContents];
+        
+        if ([self isLocallyCached])
+            return localFileName;
+        
+        if (!localFileName)
+

author Ciar‡n Walsh

summary Prevent confusing filenames when quick-looking files.

+
            localFileName = [[PBEasyFS tmpDirWithPrefix: sha] stringByAppendingPathComponent:path];
+

author Pieter de Bie

summary Delete temporary files when they are deallocated

+
        
+

author Pieter de Bie

summary Allow double click to open file

+
        NSFileHandle* handle = [repository handleForArguments:[NSArray arrayWithObjects:@"show", [self refSpec], nil]];
+        NSData* data = [handle readDataToEndOfFile];
+

author Pieter de Bie

summary Delete temporary files when they are deallocated

+
        [data writeToFile:localFileName atomically:YES];
+        
+        NSFileManager* fs = [NSFileManager defaultManager];
+        localMtime = [[fs attributesOfItemAtPath:localFileName error: nil] objectForKey:NSFileModificationDate];
+    
+        return localFileName;
+

author Pieter de Bie

summary Allow double click to open file

+
    }
+    
+

author Pieter de Bie

summary Add a Tree displayer

+
    - (NSArray*) children
+    {
+

author Pieter de Bie

summary Use unified interface and display tree contents

+
        if (children != nil)
+

author Pieter de Bie

summary Add a Tree displayer

+
            return children;
+        
+

author Pieter de Bie

summary Use unified interface and display tree contents

+
        NSString* ref = [self refSpec];
+

author Pieter de Bie

summary Add a Tree displayer

+
    
+        NSFileHandle* handle = [repository handleForArguments:[NSArray arrayWithObjects:@"show", ref, nil]];
+        [handle readLine];
+        [handle readLine];
+        
+        NSMutableArray* c = [NSMutableArray array];
+        
+        NSString* p = [handle readLine];
+        while (p.length > 0) {
+

author Nathan Kinsinger

summary Bugfix: Avoid looping while parsing the children of a tree

+
            if ([p isEqualToString:@"\r"])
+                break;
+    
+

author Pieter de Bie

summary Add a Tree displayer

+
            BOOL isLeaf = ([p characterAtIndex:p.length - 1] != '/');
+            if (!isLeaf)
+                p = [p substringToIndex:p.length -1];
+    
+            PBGitTree* child = [PBGitTree treeForTree:self andPath:p];
+            child.leaf = isLeaf;
+            [c addObject: child];
+            
+            p = [handle readLine];
+        }
+        children = c;
+        return c;
+    }
+    
+    - (NSString*) fullPath
+    {
+        if (!parent)
+            return @"";
+        
+        if ([parent.fullPath isEqualToString:@""])
+            return self.path;
+        
+        return [parent.fullPath stringByAppendingPathComponent: self.path];
+    }
+    
+

author Pieter de Bie

summary Delete temporary files when they are deallocated

+
    - (void) finalize
+    {
+        if (localFileName)
+            [[NSFileManager defaultManager] removeFileAtPath:localFileName handler:nil];
+        [super finalize];
+    }
+

author German Laullon

summary HTML Blame

+
    
+    +(NSString *)parseBlame:(NSString *)string
+    {
+        string=[string stringByReplacingOccurrencesOfString:@"<" withString:@"<"];
+        string=[string stringByReplacingOccurrencesOfString:@">" withString:@">"];
+

author German Laullon

summary HTML Blame

+
        
+        NSArray *lines = [string componentsSeparatedByString:@"\n"];
+        NSString *line;
+        NSMutableDictionary *headers=[NSMutableDictionary dictionary];
+        NSMutableString *res=[NSMutableString string];
+        
+        [res appendString:@"<table class='blocks'>\n"];
+        int i=0;
+        while(i<[lines count]){
+            line=[lines objectAtIndex:i];
+            NSArray *header=[line componentsSeparatedByString:@" "];
+            if([header count]==4){
+                int nLines=[(NSString *)[header objectAtIndex:3] intValue];
+                [res appendFormat:@"<tr class='block l%d'>\n",nLines];
+                line=[lines objectAtIndex:++i];
+                if([[[line componentsSeparatedByString:@" "] objectAtIndex:0] isEqual:@"author"]){
+                    NSString *author=line;
+                    NSString *summary=nil;
+                    while(summary==nil){
+                        line=[lines objectAtIndex:i++];
+                        if([[[line componentsSeparatedByString:@" "] objectAtIndex:0] isEqual:@"summary"]){
+                            summary=line;
+                        }
+                    }
+                    NSString *block=[NSString stringWithFormat:@"<td><p class='author'>%@</p><p class='summary'>%@</p></td>\n<td>\n",author,summary];
+                    [headers setObject:block forKey:[header objectAtIndex:0]];
+                }
+                [res appendString:[headers objectForKey:[header objectAtIndex:0]]];
+                
+

author Not Committed Yet

summary Version of PBGitTree.m from PBGitTree.m

+
                NSMutableString *code=[NSMutableString string];
+

author German Laullon

summary HTML Blame

+
                do{
+                    line=[lines objectAtIndex:i++];
+                }while([line characterAtIndex:0]!='\t');
+                line=[line stringByReplacingOccurrencesOfString:@"\t" withString:@"    "];
+

author Not Committed Yet

summary Version of PBGitTree.m from PBGitTree.m

+
                [code appendString:line];
+                [code appendString:@"\n"];
+

author German Laullon

summary HTML Blame

+
                
+                int n;
+                for(n=1;n<nLines;n++){
+                    line=[lines objectAtIndex:i++];
+                    NSArray *h=[line componentsSeparatedByString:@" "];
+                    do{
+                        line=[lines objectAtIndex:i++];
+                    }while([line characterAtIndex:0]!='\t');
+                    line=[line stringByReplacingOccurrencesOfString:@"\t" withString:@"    "];
+

author Not Committed Yet

summary Version of PBGitTree.m from PBGitTree.m

+
                    [code appendString:line];
+                    [code appendString:@"\n"];
+

author German Laullon

summary HTML Blame

+
                }
+

author Not Committed Yet

summary Version of PBGitTree.m from PBGitTree.m

+
                [res appendFormat:@"<pre class='first-line: %@;brush: js'>%@</pre>",[header objectAtIndex:2],code];
+                [res appendString:@"</td>\n"];
+

author German Laullon

summary HTML Blame

+
            }else{
+                break;
+            }
+            [res appendString:@"</tr>\n"];
+        }  
+        [res appendString:@"</table>\n"];
+

author Not Committed Yet

summary Version of PBGitTree.m from PBGitTree.m

+
        NSLog(@"%@",res);
+

author German Laullon

summary HTML Blame

+
    
+        return (NSString *)res;
+    }
+

author Pieter de Bie

summary Add a Tree displayer

+
    @end
+
+ + + \ No newline at end of file diff --git a/html/views/commit/test.html b/html/views/commit/test.html new file mode 100644 index 000000000..4f870000e --- /dev/null +++ b/html/views/commit/test.html @@ -0,0 +1,95 @@ + + Diff for file + + + + + + + + + + +

+ + Context: + Nothing to commit + +

+ + + + +
+ Nothing to commit (working directory clean) +
+
PBWebChangesController.m
... +12 +13 +14 + + +15 +16 +17 +... +72 +73 +74 +75 + + +76 + + +77 +78 +79 +... +85 +86 +87 +88 + +89 +90 +91 +
... +12 +13 +14 +15 +16 +17 +18 +19 +... +74 +75 +76 + +77 +78 +79 +80 +81 +82 +83 +84 +... +90 +91 +92 + +93 +94 +95 +96 +
DiscardStage@@ -12,6 +12,8 @@
@implementation PBWebChangesController
+@synthesize fileViewerController;
+
- (void) awakeFromNib
{
selectedFile = nil;
DiscardStage@@ -72,8 +74,11 @@ -(IBAction)displayControlChanged:(id)sender{
- (void) refresh
{
- if (!finishedLoading)
+ [fileViewerController showFile:[selectedFile path] sha:nil];
+ /*if (!finishedLoading)
return;
+
+ [fileViewerController showFile:selectedFile sha:@""];
id script = [view windowScriptObject];
DiscardStage@@ -85,7 +90,7 @@ - (void) refresh
[script callWebScriptMethod:@"showFileBlame"
withArguments:[NSArray arrayWithObjects:selectedFile ?: (id)[NSNull null],
[NSNumber numberWithBool:selectedFileIsCached], nil]];
- }
+ }*/
}
- (void)stageHunk:(NSString *)hunk reverse:(BOOL)reverse
+ + \ No newline at end of file diff --git a/html/views/history/history.css b/html/views/history/history.css index 7d855aa55..9be98367e 100644 --- a/html/views/history/history.css +++ b/html/views/history/history.css @@ -77,32 +77,6 @@ a.servicebutton { padding-top: 10px; } -#files { - margin-top: 1em; - margin-left: 0.5em; -} - -#files a { - color: #666666; - text-decoration: none; -} - -#files a:hover { - color: #4444ff; - border-bottom: 1px solid #4444ff; -} - -#files img { - float: left; - margin-right: 0.5em; - margin-top: 1px; -} - -#files p { - margin-top: 0.25em; - margin-bottom: 0.25em; -} - .clear_both { clear:both; display:block; @@ -132,7 +106,19 @@ a { a.showdiff { text-decoration: none; + text-align: center; font-size: 1.3em; + background-color: #d5d5d5; + display: block; + padding: 5px 10px; + width: 400px; + margin: 25px auto 25px auto; + -webkit-border-radius: 10px; + border: 1px solid #999; + -webkit-box-shadow: 1px 1px 2px #DDD; +} +a.showdiff:hover { + background-color: #c4daff; } .refs { @@ -159,6 +145,117 @@ a.showdiff { .refs.currentBranch { background-color: #fca64f; } +#message_files { + margin: 0; + padding: 0; +} +#message { + padding: 10px; +} +#files { + margin: 10px; + padding: 10px 0; + background-color: #f9f9f9; + -webkit-border-radius: 10px; + border: 1px solid #CCC; + -webkit-box-shadow: 1px 1px 2px #DDD; +} +#files ul { + margin: 0; + padding: 0; +} +#files li { + list-style-type: none; + padding: 4px 10px; + color: #000; + height: 18px; +} +#files li.odd { + background-color: #f9f9f9; +} +#files li.even { + background-color: #f0f0f0; +} +#files li:hover { + background-color: #d4e4ff; + cursor: pointer; +} +#files img.changetype-icon { + float: left; + margin: 3px 7px; +} +#files li .filename { + position: relative; + top: 2px; + font-size: 12px; + text-shadow: white 0 0 4px; + z-index: 100; +} + +#files .diffstat-info { + padding: 0; + position: absolute; + right: 30px; + display: inline-block; +} +#files .changes-bar { + height: 6px; + display: inline-block; + right: 46px; + position: absolute; + -webkit-border-top-left-radius: 5px; + -webkit-border-bottom-left-radius: 5px; + -webkit-box-shadow: 0 1px 1px #fff; +} +#files .changes-bar.added { + background-color: #1a843d; + border: 1px solid #164b27; +} +#files .changes-bar.removed { + top: 10px; + background-color: #ce2e2b; + border: 1px solid #8c1815; +} + +#files .diffstat-numbers { + background-color: rgba(0,0,0,.75); + color: #e3e3e3; + font-size: 12px; + font-weight: bold; + text-shadow: #000 0 1px 1px; + white-space: nowrap; + padding: 1px 5px; + position: absolute; + height: 14px; + border: 1px solid black; + -webkit-box-shadow: 0 1px 1px white; +} + +#files .diffstat-numbers.summary { + -webkit-border-top-right-radius: 12px; + -webkit-border-bottom-right-radius: 12px; + right: 0px; + width: 35px; +} +#files .diffstat-numbers.details { + -webkit-border-top-left-radius: 12px; + -webkit-border-bottom-left-radius: 12px; + right: 46px; + text-align: right; +} +#files .diffstat-numbers.details .added { + color: #5db15d; +} +#files .diffstat-numbers.details .removed { + color: #f34b4e; +} + +#files .diffstat-numbers.binary { + display: inline-block; + right: 30px; + -webkit-border-radius: 12px; + background-color: #a7681f; +} /* div.button diff --git a/html/views/history/history.js b/html/views/history/history.js index 225e590fd..d030efec5 100644 --- a/html/views/history/history.js +++ b/html/views/history/history.js @@ -1,4 +1,5 @@ -var commit; +var commit, + fileElementPrototype; // Create a new Commit object // obj: PBGitCommit object @@ -16,39 +17,124 @@ var Commit = function(obj) { // TODO: // this.author_date instant - // This can be called later with the output of - // 'git show' to fill in missing commit details (such as a diff) - this.parseDetails = function(details) { - this.raw = details; + this.parseSummary = function(summaryString) { + this.summaryRaw = summaryString; + + // Get the header info and the full commit message + var messageStart = this.summaryRaw.indexOf("\n\n") + 2; + this.header = this.summaryRaw.substring(0, messageStart); + var afterHeader = this.summaryRaw.substring(messageStart); + var numstatStart = afterHeader.indexOf("\n\n") + 2; + if (numstatStart > 1) { + this.message = afterHeader.substring(0, numstatStart).replace(/^ /gm, "").escapeHTML();; + var afterMessage = afterHeader.substring(numstatStart); + var filechangeStart = afterMessage.indexOf("\n ") + 1; + if (filechangeStart > 1) { + this.numstatRaw = afterMessage.substring(0, filechangeStart); + this.filechangeRaw = afterMessage.substring(filechangeStart); + } + else { + this.numstatRaw = afterMessage; + this.filechangeRaw = ""; + } + } + else { + this.message = afterHeader; + this.numstatRaw = ""; + this.filechangeRaw = ""; + } + + + if (typeof this.header !== 'undefined') { + var matches = this.header.match(/\nauthor (.*) <(.*@.*|.*)> ([0-9].*)/); + if (matches !== null && matches[2] !== null) { + if (!(matches[2].match(/@[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}/))) + this.author_email = matches[2]; + + if (typeof matches[3] !== 'undefined') + this.author_date = new Date(parseInt(matches[3]) * 1000); + + matches = this.header.match(/\ncommitter (.*) <(.*@.*|.*)> ([0-9].*)/); + if (typeof matches[2] !== 'undefined') + this.committer_email = matches[2]; + if (typeof matches[3] !== 'undefined') + this.committer_date = new Date(parseInt(matches[3]) * 1000); + } + } + + // Parse the --numstat part to get the list of files and lines changed. + this.filesInfo = []; + var lines = this.numstatRaw.split('\n'); + for (var lineno=0; lineno < lines.length; lineno++) { + var columns = lines[lineno].split('\t'); + if (columns.length >= 2) { + if (columns[0] == "-" && columns[1] == "-") { + this.filesInfo.push({"filename": columns[2], + "changeType": "modified", + "binary": true}); + } + else { + this.filesInfo.push({"numLinesAdded": parseInt(columns[0]), + "numLinesRemoved": parseInt(columns[1]), + "filename": columns[2], + "changeType": "modified", + "binary": false}); + } + } + } + + // Parse the filechange part (from --summary) to get info about files + // that were added/deleted/etc. + // Sample text: + // create mode 100644 GitXTextFieldCell.h + // delete mode 100644 someDir/filename with spaces.txt + // rename fielname with spaces.txt => filename_without_spaces.txt (98%) + var lines = this.filechangeRaw.split('\n'); + for (var lineno=0; lineno < lines.length; lineno++) { + var line = lines[lineno]; + var filename="", changeType=""; + if (line.indexOf("delete") == 1) { + filename = line.match(/ delete mode \d+ (.*)$/)[0]; + changeType = "removed"; + } + else if (line.indexOf("create") == 1) { + filename = line.match(/ create mode \d+ (.*)/)[0]; + changeType = "added"; + } + else if (line.indexOf("rename") == 1) { + // get the new name of the file (the part after the " => ") + filename = line.match(/ rename (.*) \(.+\)$/)[0]; + changeType = "renamed"; + } + + if (filename != "") { + // Update the appropriate filesInfo with the actual changeType. + for (var i=0; i < commit.filesInfo.length; i+=1) { + if (commit.filesInfo[i].filename == filename) { + commit.filesInfo[i].changeType = changeType; + if (changeType == "renamed") { + var names = filename.split(" => "); + commit.filesInfo[i].oldFilename = names[0]; + commit.filesInfo[i].newFilename = names[1]; + } + break; + } + } + } + } + } - var diffStart = this.raw.indexOf("\ndiff "); - var messageStart = this.raw.indexOf("\n\n") + 2; + // This can be called later with the output of + // 'git show' to get the full diff + this.parseFullDiff = function(fullDiff) { + this.fullDiffRaw = fullDiff; + var diffStart = this.fullDiffRaw.indexOf("\ndiff "); if (diffStart > 0) { - this.message = this.raw.substring(messageStart, diffStart).replace(/^ /gm, "").escapeHTML(); - this.diff = this.raw.substring(diffStart); + this.diff = this.fullDiffRaw.substring(diffStart); } else { - this.message = this.raw.substring(messageStart).replace(/^ /gm, "").escapeHTML(); this.diff = ""; } - this.header = this.raw.substring(0, messageStart); - - if (typeof this.header !== 'undefined') { - var match = this.header.match(/\nauthor (.*) <(.*@.*|.*)> ([0-9].*)/); - if (typeof match !== 'undefined' && typeof match[2] !== 'undefined') { - if (!(match[2].match(/@[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}/))) - this.author_email = match[2]; - - if (typeof match[3] !== 'undefined') - this.author_date = new Date(parseInt(match[3]) * 1000); - - match = this.header.match(/\ncommitter (.*) <(.*@.*|.*)> ([0-9].*)/); - if (typeof match[2] !== 'undefined') - this.committer_email = match[2]; - if (typeof match[3] !== 'undefined') - this.committer_date = new Date(parseInt(match[3]) * 1000); - } - } } this.reloadRefs = function() { @@ -57,6 +143,13 @@ var Commit = function(obj) { }; +var extractPrototypes = function() { + // Grab an element from the DOM, save it in a global variable (with its + // id removed) so it can be copied later, and remove it from the DOM. + fileElementPrototype = $('file_prototype'); + fileElementPrototype.removeAttribute('id'); + fileElementPrototype.parentNode.removeChild(fileElementPrototype); +} var confirm_gist = function(confirmation_message) { if (!Controller.isFeatureEnabled_("confirmGist")) { @@ -169,9 +262,9 @@ var showRefs = function() { var loadCommit = function(commitObject, currentRef) { // These are only the things we can do instantly. - // Other information will be loaded later by loadCommitDetails, - // Which will be called from the controller once - // the commit details are in. + // Other information will be loaded later by loadCommitSummary + // and loadCommitFullDiff, which will be called from the + // controller once the commit details are in. if (commit && commit.notificationID) clearTimeout(commit.notificationID); @@ -182,10 +275,13 @@ var loadCommit = function(commitObject, currentRef) { $("commitID").innerHTML = commit.sha; $("authorID").innerHTML = commit.author_name; $("subjectID").innerHTML = commit.subject.escapeHTML(); - $("diff").innerHTML = "" - $("message").innerHTML = "" - $("files").innerHTML = "" - $("date").innerHTML = "" + $("diff").innerHTML = ""; + $("message").innerHTML = ""; + $("date").innerHTML = ""; + $("files").style.display = "none"; + var filelist = $("filelist"); + while (filelist.hasChildNodes()) + filelist.removeChild(filelist.lastChild); showRefs(); for (var i = 0; i < $("commit_header").rows.length; ++i) { @@ -219,51 +315,7 @@ var loadCommit = function(commitObject, currentRef) { var showDiff = function() { - $("files").innerHTML = ""; - // Callback for the diff highlighter. Used to generate a filelist - var newfile = function(name1, name2, id, mode_change, old_mode, new_mode) { - var img = document.createElement("img"); - var p = document.createElement("p"); - var link = document.createElement("a"); - link.setAttribute("href", "#" + id); - p.appendChild(link); - var finalFile = ""; - if (name1 == name2) { - finalFile = name1; - img.src = "../../images/modified.png"; - img.title = "Modified file"; - p.title = "Modified file"; - if (mode_change) - p.appendChild(document.createTextNode(" mode " + old_mode + " -> " + new_mode)); - } - else if (name1 == "/dev/null") { - img.src = "../../images/added.png"; - img.title = "Added file"; - p.title = "Added file"; - finalFile = name2; - } - else if (name2 == "/dev/null") { - img.src = "../../images/removed.png"; - img.title = "Removed file"; - p.title = "Removed file"; - finalFile = name1; - } - else { - img.src = "../../images/renamed.png"; - img.title = "Renamed file"; - p.title = "Renamed file"; - finalFile = name2; - p.insertBefore(document.createTextNode(name1 + " -> "), link); - } - - link.appendChild(document.createTextNode(finalFile)); - link.setAttribute("representedFile", finalFile); - - p.insertBefore(img, link); - $("files").appendChild(p); - } - var binaryDiff = function(filename) { if (filename.match(/\.(png|jpg|icns|psd)$/i)) return 'Display image'; @@ -271,7 +323,7 @@ var showDiff = function() { return "Binary file differs"; } - highlightDiff(commit.diff, $("diff"), { "newfile" : newfile, "binaryFile" : binaryDiff }); + highlightDiff(commit.diff, $("diff"), { "binaryFile" : binaryDiff }); } var showImage = function(element, filename) @@ -296,27 +348,27 @@ var enableFeatures = function() enableFeature("gravatar", $("committer_gravatar").parentNode) } -var loadCommitDetails = function(data) +var loadCommitSummary = function(data) { - commit.parseDetails(data); - + commit.parseSummary(data); + if (commit.notificationID) clearTimeout(commit.notificationID) - else - $("notification").style.display = "none"; - + else + $("notification").style.display = "none"; + var formatEmail = function(name, email) { return email ? name + " <" + email + ">" : name; } - + $("authorID").innerHTML = formatEmail(commit.author_name, commit.author_email); $("date").innerHTML = commit.author_date; setGravatar(commit.author_email, $("author_gravatar")); - + if (commit.committer_name != commit.author_name) { $("committerID").parentNode.style.display = ""; $("committerID").innerHTML = formatEmail(commit.committer_name, commit.committer_email); - + $("committerDate").parentNode.style.display = ""; $("committerDate").innerHTML = commit.committer_date; setGravatar(commit.committer_email, $("committer_gravatar")); @@ -324,13 +376,111 @@ var loadCommitDetails = function(data) $("committerID").parentNode.style.display = "none"; $("committerDate").parentNode.style.display = "none"; } - + $("message").innerHTML = commit.message.replace(/\n/g,"
"); + if (commit.filesInfo.length > 0) { + // Create the file list + for (var i=0; i < commit.filesInfo.length; i+=1) { + var fileInfo = commit.filesInfo[i]; + var fileElem = fileElementPrototype.cloneNode(true); // this is a
  • + fileElem.targetFileId = "file_index_"+i; + + var displayName, representedFile; + if (fileInfo.changeType == "renamed") { + displayName = fileInfo.filename; + representedFile = fileInfo.newFilename; + } + else { + displayName = fileInfo.filename; + representedFile = fileInfo.filename; + } + fileElem.title = fileInfo.changeType + ": " + displayName; // set tooltip + fileElem.setAttribute("representedFile", representedFile); + + if (i % 2) + fileElem.className += "even"; + else + fileElem.className += "odd"; + fileElem.onclick = function () { + // Show the full diff in case it's not already visisble. + showDiff(); + // Scroll to that file. + $(this.targetFileId).scrollIntoView(true); + } + + // Start with a modified icon, and update it later when the + // `diff --summary` info comes back. + var imgElement = fileElem.getElementsByClassName("changetype-icon")[0]; + imgElement.src = "../../images/"+fileInfo.changeType+".png"; + + var filenameElement = fileElem.getElementsByClassName("filename")[0]; + filenameElement.innerText = displayName; + + var diffstatElem = fileElem.getElementsByClassName("diffstat-info")[0]; + var binaryElem = fileElem.getElementsByClassName("binary")[0] + if (fileInfo.binary) { + // remove the diffstat-info element + diffstatElem.parentNode.removeChild(diffstatElem); + } + else { + // remove the binary element + binaryElem.parentNode.removeChild(binaryElem); + + // Show the num of lines added/removed + var addedWidth = 2 * fileInfo.numLinesAdded; + var removedWidth = 2 * fileInfo.numLinesRemoved; + // Scale them down proportionally if they're too wide. + var maxWidth = 350; + var minWidth = 5; + if (addedWidth+removedWidth > maxWidth) { + var scaleBy = maxWidth/(addedWidth+removedWidth); + addedWidth *= scaleBy; + removedWidth *= scaleBy; + } + if (addedWidth > 0 && addedWidth < minWidth) addedWidth = minWidth; + if (removedWidth > 0 && removedWidth < minWidth) removedWidth = minWidth; + + // show lines changed info + var numLinesAdded = fileInfo.numLinesAdded; + var numLinesRemoved = fileInfo.numLinesRemoved; + var numLinesChanged = numLinesAdded + numLinesRemoved; + // summarize large numbers + if (numLinesChanged > 999) numLinesChanged = "~" + Math.round(numLinesChanged / 1000) + "k"; + // fill in numbers + var diffstatSummary = diffstatElem.getElementsByClassName("diffstat-numbers")[1]; + diffstatSummary.innerText = numLinesChanged; + var diffstatDetails = diffstatElem.getElementsByClassName("diffstat-numbers")[0]; + diffstatDetails.getElementsByClassName("added")[0].innerText = "+"+numLinesAdded; + diffstatDetails.getElementsByClassName("removed")[0].innerText = "-"+numLinesRemoved; + + // Size the bars + var addedBar = diffstatElem.getElementsByClassName("changes-bar")[0]; + if (addedWidth >= minWidth) + addedBar.style.width = addedWidth; + else + addedBar.style.visibility = "hidden"; + + var removedBar = diffstatElem.getElementsByClassName("changes-bar")[1]; + if (removedWidth >= minWidth) + removedBar.style.width = removedWidth; + else + removedBar.style.visibility = "hidden"; + } + $("filelist").appendChild(fileElem); + } + $("files").style.display = ""; + } +} + +var loadCommitFullDiff = function(data) +{ + commit.parseFullDiff(data); + if (commit.diff.length < 200000) showDiff(); else - $("diff").innerHTML = "This is a large commit. Click here or press 'v' to view."; + $("diff").innerHTML = "This is a large commit.
    Click here or press 'v' to view.
    "; hideNotification(); enableFeatures(); diff --git a/html/views/history/index.html b/html/views/history/index.html index cc8d4a286..7b6ac5aca 100644 --- a/html/views/history/index.html +++ b/html/views/history/index.html @@ -9,9 +9,38 @@ + - +
    @@ -67,7 +96,24 @@
    -
    +
    +
      +
    • + + +
      binary
      +
      +
      +
      + +
      +
      +
    • +
    +
    diff --git a/html/views/source/index.html b/html/views/source/index.html new file mode 100644 index 000000000..fc4effe86 --- /dev/null +++ b/html/views/source/index.html @@ -0,0 +1,14 @@ + + + + + + + + + + + +
    + + \ No newline at end of file diff --git a/html/views/source/source.css b/html/views/source/source.css new file mode 100644 index 000000000..e69de29bb diff --git a/html/views/source/source.js b/html/views/source/source.js new file mode 100644 index 000000000..2168e9e9f --- /dev/null +++ b/html/views/source/source.js @@ -0,0 +1,9 @@ +var showFile = function(txt) { + $("source").style.display = ""; + $("source").innerHTML="
    "+txt+"
    "; + + SyntaxHighlighter.defaults['toolbar'] = false; + SyntaxHighlighter.highlight(); + + return; +} diff --git a/scripts/install.sh b/scripts/install.sh new file mode 100755 index 000000000..e9df72306 --- /dev/null +++ b/scripts/install.sh @@ -0,0 +1,57 @@ +#!/bin/sh + +# untitled.sh +# GitX +# +# Created by Andre Berg on 19.10.09. +# Copyright 2009 Berg Media. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Installs GitX.app to $CUSTOM_INSTALL_DIR from Install.xcconfig +# Installs gitx to $CLI_CUSTOM_INSTALL_DIR from Install.xcconfig using gitx_askpasswd + +export SUDO_ASKPASS="$BUILT_PRODUCTS_DIR/gitx_askpasswd" +export GITX_ASKPASSWD_DIALOG_TITLE="Please enter sudo pass for Install" + +if [[ $BUILD_STYLE =~ "Install" ]]; then + if [[ $TARGET_NAME =~ "$PRODUCT_NAME" ]]; then + # install GitX + echo "Installing ${FULL_PRODUCT_NAME} to ${CUSTOM_INSTALL_DIR}... " + echo "(switch to build config other than Install to avoid)" + if [[ ! -d "$CUSTOM_INSTALL_DIR" ]]; then + echo "$CUSTOM_INSTALL_DIR doesn't exist. Will create it for you..." + sudo -A -E /bin/mkdir -p "${CUSTOM_INSTALL_DIR}" + fi + if [[ -e /opt/local/bin/rsync ]]; then + /opt/local/bin/rsync -rlHEptog --xattrs --acls "$BUILT_PRODUCTS_DIR/$FULL_PRODUCT_NAME" "$CUSTOM_INSTALL_DIR/" + else + /usr/bin/rsync -rlHEptog "$BUILT_PRODUCTS_DIR/$FULL_PRODUCT_NAME" "$CUSTOM_INSTALL_DIR/" + fi + elif [[ $TARGET_NAME =~ "cli tool" ]]; then + # install cli tool + echo "Installing gitx command line tool to ${CLI_CUSTOM_INSTALL_DIR}..." + echo "(switch to build config other than Install to avoid)" + if [[ ! -d "$CLI_CUSTOM_INSTALL_DIR" ]]; then + echo "$CLI_CUSTOM_INSTALL_DIR doesn't exist. Will create it for you..." + sudo -A -E /bin/mkdir -p "${CLI_CUSTOM_INSTALL_DIR}" + fi + if [[ -e /opt/local/bin/rsync ]]; then + sudo -A -E /opt/local/bin/rsync -rlHEptog --xattrs --acls "$BUILT_PRODUCTS_DIR/gitx" "$CLI_CUSTOM_INSTALL_DIR/" + else + sudo -A -E /usr/bin/rsync -rlHEptog "$BUILT_PRODUCTS_DIR/gitx" "$CLI_CUSTOM_INSTALL_DIR/" + fi + fi +else + echo '$BUILD_STYLE does not contain "Install"... nothing to copy' +fi \ No newline at end of file diff --git a/source.css b/source.css new file mode 100644 index 000000000..e69de29bb