diff --git a/Rakefile b/Rakefile
new file mode 100644
index 0000000..18cf3e6
--- /dev/null
+++ b/Rakefile
@@ -0,0 +1,17 @@
+require 'rubygems'
+
+desc "Default task (builds mojo.el)"
+task :default => [:build]
+
+desc "Build mojo.el"
+task :build do
+ Dir.chdir 'src'
+ require 'assemble'
+ begin
+ Assembler.go!
+ puts "XD"
+ rescue RuntimeError => e
+ puts ":("
+ raise e.inspect
+ end
+end
diff --git a/mojo.el b/mojo.el
index ce8ee93..e3354fb 100644
--- a/mojo.el
+++ b/mojo.el
@@ -1,5 +1,5 @@
-;;; mojo.el --- Interactive functions to aid the development of webOS apps
-;; 2009-11-22 22:23:26
+;;; ../mojo.el --- Interactive functions to aid the development of webOS apps
+;; 2009-11-24 09:51:16
(defconst mojo-version "0.9.4")
(require 'json)
@@ -114,68 +114,6 @@ ideas. Send me a pull request on github if you hack on mojo.el.")
;; Run Mojo in debug mode. Assumed true while in such an early version.
;; default = t
-;; CHANGELOG
-;; =========
-;;
-;; sjs 2009-11-22
-;; v 0.9.4 launch emulator if needed
-;;
-;; - Commands that use the emulator launch it if necessary and wait till
-;; it is fully booted before running commands.
-;;
-;; sjs 2009-11-21
-;; v 0.9.3 (one more bug fix for today)
-;;
-;; - Don't pass -d switch to commands that don't accept it.
-;;
-;; sjs 2009-11-21
-;; v 0.9.2 (bug fixes)
-;;
-;; - reading json files no longer messes up your buffer history.
-;;
-;; - app list completion works now (caching bug)
-;;
-;; sjs 2009-11-21
-;; v 0.9.1
-;;
-;; - Added mojo-package-install-and-launch.
-;;
-;; - New variable for specifying whether commands target the
-;; device or emulator, *mojo-target*. Set it to 'usb' for a
-;; real device and 'tcp' for the emulator. Defaults to 'tcp'.
-;; To set the default target you can use the convenience
-;; functions mojo-target-device and mojo-target-emulator.
-;;
-;; sjs 2009-11-20
-;; v 0.9
-;;
-;; - Automatically find Mojo project root by searching upwards
-;; for appinfo.json.
-;;
-;; - Added command for generating new scenes,
-;; mojo-generate-scene.
-;;
-;; - mojo-package now operates only on the current project.
-;;
-;; - Parse appinfo.json to get version, used for installing &
-;; launching with less interaction.
-;;
-;; - mojo-install, mojo-launch, mojo-inspect, and mojo-delete
-;; still read in arguments but have the current project/app as
-;; the default values.
-;;
-;; - New convenience method: mojo-package-install-and-inspect
-;; This function only operates on the active app and does not
-;; read in any input.
-;;
-;; - Remembered filenames and app ids are cleared when the Mojo
-;; project root changes. (DWIM)
-;;
-;; - Parse output of `palm-install --list` for app id
-;; completion. App id completion was ported from cheat.el.
-;;
-;; v 0.2 - Fixed some minor bugs
-;; v 0.1 - Initial release
;;; Code:
diff --git a/src/assemble.rb b/src/assemble.rb
new file mode 100755
index 0000000..56b63de
--- /dev/null
+++ b/src/assemble.rb
@@ -0,0 +1,79 @@
+#!/usr/bin/env ruby
+
+require 'rubygems'
+require 'json'
+require 'blankslate'
+
+class Assembler
+
+ # When you insert lambdas into a LazyHash they are automatically executed
+ # when the value is retrieved, and the result is cached in place of the
+ # original lambda value.
+ class LazyHash < Hash
+ alias_method :original_get, :[]
+ def [](key)
+ if value = original_get(key)
+ if value.respond_to?(:call)
+ self[key] = value.call
+ original_get(key)
+ else
+ value
+ end
+ end
+ end
+ end
+
+ class AssemblerDelegate < BlankSlate
+ attr_reader :app, :files
+ def initialize(app, files)
+ @app = app
+ @files = files
+ end
+ def timestamp
+ Time.now.strftime('%Y-%m-%d %H:%M:%S')
+ end
+ def process(string)
+ eval(string)
+ end
+ end
+
+ attr_reader :app, :files, :delegate
+
+ def self.go!
+ new.write_output!
+ end
+
+ def initialize
+ @app = JSON.parse(File.read('info.json'))
+ @files = Dir['*'].select {|f| File.file?(f)}.
+ inject(LazyHash.new) {|h,f|
+ h[f] = lambda { read_file(f) };h
+ }
+ @delegate = AssemblerDelegate.new(@app, @files)
+ end
+
+ def read_file(f)
+ if f[-3,3] == '.el'
+ File.read(f)
+ else
+ File.readlines(f).map{|l| ";; #{l}"}.join
+ end
+ end
+
+ def write_output!
+ output = File.read(app['template']).
+ gsub(/#\{[^}]+\}/) {|m| @delegate.process(m[2..-2])}
+ File.open(app['filename'], 'w') {|f| f.puts(output)}
+ end
+
+end
+
+if __FILE__ == $0
+ begin
+ Assembler.go!
+ puts "XD"
+ rescue RuntimeError => e
+ puts e.inspect
+ puts ":("
+ end
+end
\ No newline at end of file
diff --git a/src/blankslate.rb b/src/blankslate.rb
new file mode 100644
index 0000000..2ea2ad8
--- /dev/null
+++ b/src/blankslate.rb
@@ -0,0 +1,113 @@
+#!/usr/bin/env ruby
+#--
+# Copyright 2004, 2006 by Jim Weirich (jim@weirichhouse.org).
+# All rights reserved.
+
+# Permission is granted for use, copying, modification, distribution,
+# and distribution of modified versions of this work as long as the
+# above copyright notice is included.
+#++
+
+######################################################################
+# BlankSlate provides an abstract base class with no predefined
+# methods (except for \_\_send__ and \_\_id__).
+# BlankSlate is useful as a base class when writing classes that
+# depend upon method_missing (e.g. dynamic proxies).
+#
+class BlankSlate
+ class << self
+
+ # Hide the method named +name+ in the BlankSlate class. Don't
+ # hide +instance_eval+ or any method beginning with "__".
+ def hide(name)
+ if instance_methods.include?(name.to_s) and
+ name !~ /^(__|instance_eval)/
+ @hidden_methods ||= {}
+ @hidden_methods[name.to_sym] = instance_method(name)
+ undef_method name
+ end
+ end
+
+ def find_hidden_method(name)
+ @hidden_methods ||= {}
+ @hidden_methods[name] || superclass.find_hidden_method(name)
+ end
+
+ # Redefine a previously hidden method so that it may be called on a blank
+ # slate object.
+ def reveal(name)
+ bound_method = nil
+ unbound_method = find_hidden_method(name)
+ fail "Don't know how to reveal method '#{name}'" unless unbound_method
+ define_method(name) do |*args|
+ bound_method ||= unbound_method.bind(self)
+ bound_method.call(*args)
+ end
+ end
+ end
+
+ instance_methods.each { |m| hide(m) }
+end
+
+######################################################################
+# Since Ruby is very dynamic, methods added to the ancestors of
+# BlankSlate after BlankSlate is defined will show up in the
+# list of available BlankSlate methods. We handle this by defining a
+# hook in the Object and Kernel classes that will hide any method
+# defined after BlankSlate has been loaded.
+#
+module Kernel
+ class << self
+ alias_method :blank_slate_method_added, :method_added
+
+ # Detect method additions to Kernel and remove them in the
+ # BlankSlate class.
+ def method_added(name)
+ result = blank_slate_method_added(name)
+ return result if self != Kernel
+ BlankSlate.hide(name)
+ result
+ end
+ end
+end
+
+######################################################################
+# Same as above, except in Object.
+#
+class Object
+ class << self
+ alias_method :blank_slate_method_added, :method_added
+
+ # Detect method additions to Object and remove them in the
+ # BlankSlate class.
+ def method_added(name)
+ result = blank_slate_method_added(name)
+ return result if self != Object
+ BlankSlate.hide(name)
+ result
+ end
+
+ def find_hidden_method(name)
+ nil
+ end
+ end
+end
+
+######################################################################
+# Also, modules included into Object need to be scanned and have their
+# instance methods removed from blank slate. In theory, modules
+# included into Kernel would have to be removed as well, but a
+# "feature" of Ruby prevents late includes into modules from being
+# exposed in the first place.
+#
+class Module
+ alias blankslate_original_append_features append_features
+ def append_features(mod)
+ result = blankslate_original_append_features(mod)
+ return result if mod != Object
+ instance_methods.each do |name|
+ BlankSlate.hide(name)
+ end
+ result
+ end
+end
diff --git a/src/code.el b/src/code.el
new file mode 100644
index 0000000..811f386
--- /dev/null
+++ b/src/code.el
@@ -0,0 +1,448 @@
+
+(defcustom mojo-sdk-directory
+ (case system-type
+ ((windows-nt) "c:/progra~1/palm/sdk")
+ ((darwin) "/opt/PalmSDK/Current")
+ (t ""))
+ "Path to where the mojo SDK is.
+
+Note, using the old-school dos name of progra~1 was the only way i could make
+this work."
+ :type 'directory
+ :group 'mojo)
+
+(defcustom mojo-project-directory ""
+ "Directory where all your Mojo projects are located."
+ :type 'directory
+ :group 'mojo)
+
+(defcustom mojo-build-directory ""
+ "Directory where built projects are saved."
+ :type 'directory
+ :group 'mojo)
+
+;;* debug
+(defcustom mojo-debug t
+ "Run Mojo in debug mode. Assumed true while in such an early version."
+ :type 'boolean
+ :group 'mojo)
+
+
+;;* interactive generate
+(defun mojo-generate (title directory)
+ "Generate a new Mojo application in the `mojo-project-directory'.
+
+TITLE is the name of the application.
+DIRECTORY is the directory where the files are stored."
+ ;;TODO handle existing directories (use --overwrite)
+ (interactive "sTitle: \nsDirectory Name (inside of mojo-project-directory): \n")
+ (let ((mojo-dir (expand-file-name (concat mojo-project-directory "/" directory))))
+ (when (file-exists-p mojo-dir)
+ (error "Cannot mojo-generate onto an existing directory! (%s)" mojo-dir))
+ (make-directory mojo-dir)
+ (mojo-cmd "palm-generate" (list "-p" (format "\"{'title':'%s'}\"" title)
+ mojo-dir))
+ (find-file (concat mojo-dir "/appinfo.json"))))
+
+;;* interactive
+(defun mojo-generate-scene (name)
+ "Generate a new Mojo scene for the current application.
+
+NAME is the name of the scene."
+ (interactive "sScene Name: \n")
+ (let ((mojo-dir (mojo-root)))
+ (mojo-cmd "palm-generate" (list "-t" "new_scene"
+ "-p" (format "name=%s" name) mojo-dir))
+ (find-file (format "%s/app/assistants/%s-assistant.js" mojo-dir name))
+ (find-file (format "%s/app/views/%s/%s-scene.html" mojo-dir name name))))
+
+;;* interactive
+(defun mojo-emulate ()
+ "Launch the palm emulator."
+ (interactive)
+ (unless (mojo-emulator-running-p)
+ (mojo-cmd "palm-emulator" nil)))
+
+;;* interactive
+(defun mojo-package ()
+ "Package the current application into `MOJO-BUILD-DIRECTORY'."
+ (interactive)
+ (mojo-cmd "palm-package" (list "-o" (expand-file-name mojo-build-directory)
+ (mojo-root))))
+
+;;* interactive
+(defun mojo-install ()
+ "Install the package named by `MOJO-PACKAGE-FILENAME'. The emulator needs to be running."
+ (interactive)
+ (mojo-ensure-emulator-is-running)
+ (mojo-cmd-with-target "palm-install" (list (expand-file-name (mojo-read-package-filename))))
+ (mojo-invalidate-app-cache))
+
+;;* interactive
+(defun mojo-list ()
+ "List all installed packages."
+ (interactive)
+ (mojo-ensure-emulator-is-running)
+ (mojo-cmd-with-target "palm-install" (list "--list")))
+
+;;* interactive
+(defun mojo-delete ()
+ "Remove the current application using `MOJO-APP-ID'."
+ (interactive)
+ (mojo-ensure-emulator-is-running)
+ (mojo-cmd-with-target "palm-install" (list "-r" (mojo-read-app-id)))
+ (mojo-invalidate-app-cache))
+
+;;* interactive
+(defun mojo-ensure-emulator-is-running ()
+ "Launch the current application, and the emulator if necessary."
+ (interactive)
+ (if (string= "tcp" *mojo-target*)
+ (progn
+ (when (not (mojo-emulator-running-p))
+ (mojo-emulate)
+ (print "Launching the emulator, this will take a minute..."))
+ (while (not (mojo-emulator-responsive-p))
+ (sleep-for 3))
+ (print "Emulator has booted!"))
+ (print "Connect your device if necessary.")))
+
+;;* interactive
+(defun mojo-launch ()
+ "Launch the current application in the emulator."
+ (interactive)
+ (mojo-ensure-emulator-is-running)
+ (mojo-cmd-with-target "palm-launch" (list (mojo-read-app-id))))
+
+;;* interactive
+(defun mojo-close ()
+ "Close launched application."
+ (interactive)
+ (mojo-ensure-emulator-is-running)
+ (mojo-cmd-with-target "palm-launch" (list "-c" (mojo-read-app-id))))
+
+;;* launch interactive
+(defun mojo-inspect ()
+ "Run the DOM inspector on the current application."
+ (interactive)
+ (mojo-ensure-emulator-is-running)
+ (mojo-cmd-with-target "palm-launch" (list "-i" (mojo-read-app-id))))
+
+;;* emulator interactive
+(defun mojo-hard-reset ()
+ "Perform a hard reset, clearing all data."
+ (interactive)
+ (mojo-cmd "palm-emulator" (list "--reset")))
+
+(defun mojo-browse ()
+ "Use `browse-url' to visit your application with Palm Host."
+ (browse-url "http://localhost:8888"))
+
+
+;;* interactive
+(defun mojo-package-install-and-inspect ()
+ "Package, install, and launch the current application for inspection."
+ (interactive)
+ (mojo-package)
+ (mojo-ensure-emulator-is-running)
+ (mojo-cmd-with-target "palm-install" (list (expand-file-name (mojo-package-filename))))
+ (mojo-cmd-with-target "palm-launch" (list "-i" (mojo-app-id))))
+
+;;* interactive
+(defun mojo-package-install-and-launch ()
+ "Package, install, and launch the current application."
+ (interactive)
+ (mojo-package)
+ (mojo-ensure-emulator-is-running)
+ (mojo-cmd-with-target "palm-install" (list (expand-file-name (mojo-package-filename))))
+ (mojo-cmd-with-target "palm-launch" (list (mojo-app-id))))
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Some support functions that grok the basics of a Mojo project. ;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun drop-last-path-component (path)
+ "Get the head of a path by dropping the last component."
+ (if (< (length path) 2)
+ path
+ (substring path 0 (- (length path)
+ (length (last-path-component path))
+ 1)))) ;; subtract one more for the trailing slash
+
+(defun last-path-component (path)
+ "Get the tail of a path, i.e. the last component."
+ (if (< (length path) 2)
+ path
+ (let ((start -2))
+ (while (not (string= "/" (substring path start (+ start 1))))
+ (setq start (- start 1)))
+ (substring path (+ start 1)))))
+
+(defvar *mojo-last-root* ""
+ "Last Mojo root found by `MOJO-ROOT'.")
+
+(defun mojo-root ()
+ "Find a Mojo project's root directory starting with `DEFAULT-DIRECTORY'."
+ (let ((last-component (last-path-component default-directory))
+ (dir-prefix default-directory))
+ ;; remove last path element until we find appinfo.json
+ (while (and (not (file-exists-p (concat dir-prefix "/appinfo.json")))
+ (not (< (length dir-prefix) 2)))
+ (setq last-component (last-path-component dir-prefix))
+ (setq dir-prefix (drop-last-path-component dir-prefix)))
+
+ ;; If no Mojo root found, ask for a directory.
+ (if (< (length dir-prefix) 2)
+ (setq dir-prefix (mojo-read-root)))
+
+ ;; Invalidate cached values when changing projects.
+ (if (or (blank *mojo-last-root*)
+ (not (string= dir-prefix *mojo-last-root*)))
+ (progn
+ (setq *mojo-last-root* dir-prefix)
+ (setq *mojo-package-filename* nil)
+ (setq *mojo-app-id* nil)))
+
+ dir-prefix))
+
+(defun read-json-file (filename)
+ "Parse the JSON in FILENAME and return the result."
+ (save-excursion
+ (let ((origbuffer (current-buffer))
+ (filebuffer (find-file-noselect filename)))
+ (set-buffer filebuffer)
+ (let ((text (buffer-string)))
+ (switch-to-buffer origbuffer)
+ (json-read-from-string text)))))
+
+(defun mojo-app-version ()
+ "Parse the project version from the appinfo.json file in `MOJO-ROOT'."
+ (let ((appinfo (read-json-file (concat (mojo-root) "/appinfo.json"))))
+ (cdr (assoc 'version appinfo))))
+
+(defun mojo-app-id ()
+ "Parse the project id from the appinfo.json file in `MOJO-ROOT'."
+ (let ((appinfo (read-json-file (concat (mojo-root) "/appinfo.json"))))
+ (cdr (assoc 'id appinfo))))
+
+(defun mojo-package-filename ()
+ "Get the package filename for the specified application."
+ (format "%s/%s_%s_all.ipk" (expand-file-name mojo-build-directory)
+ (mojo-app-id) (mojo-app-version)))
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; app listing and completion ;;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defvar *mojo-app-cache-filename* nil)
+(defun mojo-app-cache-file (&optional force-reload)
+ "Cache the list of applications in a temporary file. Return the filename."
+ (when (or force-reload
+ (not *mojo-app-cache-filename*))
+ (setq *mojo-app-cache-filename* (make-temp-file "mojo-app-list-cache"))
+ (save-excursion
+ (let ((buffer (find-file-noselect *mojo-app-cache-filename*))
+ (apps (mojo-fetch-app-list)))
+ (set-buffer buffer)
+ (insert (string-join "\n" apps))
+ (basic-save-buffer)
+ (kill-buffer buffer))))
+ *mojo-app-cache-filename*)
+
+(defvar *mojo-app-id* nil
+ "Most recently used application id.")
+
+(defvar *mojo-package-filename* nil
+ "Most recently used package file.")
+
+(defvar *mojo-app-history* nil
+ "List of the most recently used application ids.")
+
+;; cache expires hourly by default
+(defvar *mojo-app-cache-ttl* 3600
+ "The minimum age of a stale cache file, in seconds.")
+
+(defvar *mojo-package-history* nil
+ "List of the most recently used package filenames.")
+
+;; this is from rails-lib.el in the emacs-rails package
+(defun string-join (separator strings)
+ "Join all STRINGS using SEPARATOR."
+ (mapconcat 'identity strings separator))
+
+(defun blank (thing)
+ "Return T if THING is nil or an empty string, otherwise nil."
+ (or (null thing)
+ (and (stringp thing)
+ (= 0 (length thing)))))
+
+(defun mojo-read-root ()
+ "Get the path to a Mojo application, prompting with completion and
+ history."
+ (read-file-name "Mojo project: " (expand-file-name (concat mojo-project-directory "/"))))
+
+(defun mojo-read-package-filename ()
+ "Get the filename of a packaged application, prompting with completion and
+ history.
+
+The app id is stored in *mojo-package-filename* unless it was blank."
+ (let* ((default (or *mojo-package-filename*
+ (mojo-package-filename)))
+ (package (read-file-name (format "Package file (default: %s): " default)
+ (concat mojo-build-directory "/") default t)))
+ (setq *mojo-package-filename* (last-path-component package))
+ (expand-file-name package)))
+
+(defun mojo-read-app-id (&optional prompt)
+ "Get the id of an existing application, prompting with completion and
+ history.
+
+The app id is stored in *mojo-app-id* unless it was blank."
+ (let* ((default (or *mojo-app-id* (mojo-app-id)))
+ (prompt (or prompt
+ (format "App id (default: %s): " default)))
+ (app-id (completing-read prompt
+ (mojo-app-list t)
+ nil
+ t
+ nil
+ '*mojo-app-history*
+ default)))
+ (when (blank app-id)
+ (setq app-id (mojo-app-id)))
+ (setq *mojo-app-id* app-id)
+ app-id))
+
+(defun mojo-app-list (&optional fetch-if-missing-or-stale)
+ "Get a list of installed Mojo applications."
+ (cond ((and (file-readable-p (mojo-app-cache-file))
+ (not (mojo-app-cache-stale-p)))
+ (save-excursion
+ (let* ((buffer (find-file (mojo-app-cache-file)))
+ (apps (split-string (buffer-string))))
+ (kill-buffer buffer)
+ apps)))
+ (fetch-if-missing-or-stale
+ (mojo-app-cache-file t) ;; force reload
+ (mojo-app-list)) ;; guaranteed cache hit this time
+ (t nil)))
+
+(defun mojo-fetch-app-list ()
+ "Fetch a fresh list of all applications."
+ (mojo-ensure-emulator-is-running)
+ (let* ((raw-list (nthcdr 7 (split-string (mojo-cmd-to-string "palm-install" (list "--list")))))
+ (apps (list))
+ (appname-regex "^[^0-9][^.]+\\(\\.[^.]+\\)+$")
+ (item (pop raw-list)))
+ (while item
+ (if (string-match appname-regex item) ;; liberal regex for simplicity
+ (push item apps)
+ (print (concat "discarding " item)))
+ (setq item (pop raw-list)))
+ (nreverse apps)))
+
+(defun mojo-app-cache-stale-p ()
+ "Non-nil if the cache in `MOJO-APP-CACHE-FILE' is more than
+ *mojo-app-cache-ttl* seconds old.
+
+If the cache file does not exist then it is considered stale."
+ (or (null (file-exists-p (mojo-app-cache-file)))
+ (let* ((now (float-time (current-time)))
+ (last-mod (float-time (sixth (file-attributes
+ (mojo-app-cache-file)))))
+ (age (- now last-mod)))
+ (> age *mojo-app-cache-ttl*))))
+
+(defun mojo-invalidate-app-cache ()
+ "Delete the app list cache."
+ (delete-file (mojo-app-cache-file)))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;;* lowlevel luna
+(defun mojo-luna-send (url data)
+ "Send something through luna.
+
+Luna-send is a program to send things like incoming calls, GPS status, SMS,
+etc. to your emulator.
+
+This is a low level Emacs interface to luna-send.
+URL is the luna url, and DATA is the data."
+ (mojo-cmd "luna-send" (list "-n" "1" url data)))
+
+(defvar *mojo-target* "tcp"
+ "Used to specify the target platform, \"usb\" for the device
+ and \"tcp\" for the emulator. Deaults to \"tcp\".")
+
+(defun mojo-target-device ()
+ "Specify that Mojo commands should target a real device.
+
+Sets `*mojo-target*' to \"usb\"."
+ (setq *mojo-target* "usb"))
+
+(defun mojo-target-emulator ()
+ "Specify that Mojo commands should target a real device.
+
+Sets `*mojo-target*' to \"tcp\"."
+ (setq *mojo-target* "tcp"))
+
+(defun mojo-emulator-running-p ()
+ "Determine if the webOS emulator is running or not.
+
+This command only works on Unix-like systems."
+ (= 0 (shell-command "ps x | fgrep 'Palm SDK' | fgrep -v fgrep >/dev/null 2>&1")))
+
+(defun mojo-emulator-responsive-p ()
+ "Determine if the webOS emulator is able to respond to commands yet
+ (i.e. if it's done booting)."
+ (= 0 (shell-command "palm-install -d tcp --list >/dev/null 2>&1")))
+
+(defun mojo-path-to-cmd (cmd)
+ "Return the absolute path to a Mojo SDK command line program."
+ (case system-type
+ ((windows-nt) (concat mojo-sdk-directory "/bin/" cmd ".bat"))
+ (t (concat mojo-sdk-directory "/bin/" cmd))))
+
+;;* lowlevel cmd
+(defun mojo-cmd (cmd args)
+ "General interface for running mojo-sdk commands.
+
+CMD is the name of the command (without path or extension) to execute.
+ Automagically shell quoted.
+ARGS is a list of all arguments to the command.
+ These arguments are NOT shell quoted."
+ (let ((cmd (mojo-path-to-cmd cmd))
+ (args (string-join " " args)))
+ (if mojo-debug (message "running %s with args %s " cmd args))
+ (shell-command (concat cmd " " args))))
+
+;;* lowlevel cmd
+(defun mojo-cmd-with-target (cmd args &optional target)
+ "General interface for running mojo-sdk commands that accept a target device.
+
+CMD is the name of the command (without path or extension) to
+ execute. Automagically shell quoted. ARGS is a list of all
+ arguments to the command. These arguments are NOT shell quoted.
+ TARGET specifies the target device, \"tcp\" or \"usb\"."
+ (let ((args (cons "-d" (cons (or target *mojo-target*) args))))
+ (mojo-cmd cmd args)))
+
+;;* lowlevel cmd
+(defun mojo-cmd-to-string (cmd args &optional target)
+ "General interface for running mojo-sdk commands and capturing the output
+ to a string.
+
+CMD is the name of the command (without path or extension) to execute.
+ Automatically shell quoted.
+ARGS is a list of all arguments to the command.
+ These arguments are NOT shell quoted."
+ (let ((cmd (mojo-path-to-cmd cmd))
+ (args (concat "-d " (or target *mojo-target*) " "
+ (string-join " " args))))
+ (if mojo-debug (message "running %s with args %s " cmd args))
+ (shell-command-to-string (concat cmd " " args))))
+
+(provide 'mojo)
diff --git a/src/info.json b/src/info.json
new file mode 100644
index 0000000..4707cc4
--- /dev/null
+++ b/src/info.json
@@ -0,0 +1,6 @@
+{
+ "title": "mojo.el",
+ "filename": "../mojo.el",
+ "version": "0.9.4",
+ "template": "template.el"
+}
\ No newline at end of file
diff --git a/src/template.el b/src/template.el
new file mode 100644
index 0000000..66004ec
--- /dev/null
+++ b/src/template.el
@@ -0,0 +1,123 @@
+;;; #{app['filename']} --- Interactive functions to aid the development of webOS apps
+;; #{timestamp}
+(defconst mojo-version "#{app['version']}")
+
+(require 'json)
+
+;; Copyright (c)2008 Jonathan Arkell. (by)(nc)(sa) Some rights reserved.
+;; 2009 Sami Samhuri
+;;
+;; Authors: Jonathan Arkell
+;; Sami Samhuri
+;;
+;; Latest version is available on github:
+;; http://github.com/samsonjs/config/blob/master/emacs.d/mojo.el
+;;
+;; With sufficient interest mojo.el will get its own repo.
+
+;; This file is not part of GNU Emacs.
+
+;; This program is free software; you can redistribute it and/or
+;; modify it under the terms of the GNU General Public License as
+;; published by the Free Software Foundation version 2.
+
+;; This program is distributed in the hope that it will be useful, but
+;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+;; General Public License for more details.
+
+;; For a copy of the GNU General Public License, search the Internet,
+;; or write to the Free Software Foundation, Inc., 59 Temple Place,
+;; Suite 330, Boston, MA 02111-1307 USA
+
+;;; Commentary:
+(defgroup mojo '()
+ "Interactive functions to aid the development of webOS apps.
+
+This package is in early beta. I am open to any contributions or
+ideas. Send me a pull request on github if you hack on mojo.el.")
+
+;;; Installation:
+;;
+;; 1. Put json.el and mojo.el somewhere in your load-path.
+;; (Use M-x show-variable RET load-path to see what your load path is.)
+;;
+;; 2. Add this to your Emacs init file: (require 'mojo)
+;;
+;; 3. Make sure you customize the variables:
+;; mojo-project-directory, mojo-sdk-directory and mojo-build-directory
+;; (Use M-x customize-group RET mojo RET)
+;;
+;; (optional)
+;;
+;; 4. I recommend that you define a few keyboard shortcuts in your Emacs init
+;; file. Maybe something like this:
+;;
+;; (global-set-key [f2] mojo-generate-scene)
+;; (global-set-key [f3] mojo-emulate)
+;; (global-set-key [f4] mojo-package)
+;; (global-set-key [f5] mojo-package-install-and-inspect)
+;;
+
+;;; Commands:
+;;
+;; Below are complete command list:
+;;
+;; `mojo-generate'
+;; Generate a new Mojo application in the `mojo-project-directory'.
+;; `mojo-generate-scene'
+;; Generate a new Mojo scene for the application in `mojo-root'.
+;; `mojo-emulate'
+;; Launch the palm emulator.
+;; `mojo-package'
+;; Package the current application.
+;; `mojo-install'
+;; Install the specified package for the current application.
+;; The emulator needs to be running.
+;; `mojo-list'
+;; List all installed packages.
+;; `mojo-delete'
+;; Remove application named APP-NAME.
+;; `mojo-launch'
+;; Launch the current application in an emulator.
+;; `mojo-close'
+;; Close launched application.
+;; `mojo-inspect'
+;; Run the dom inspector on the current application.
+;; `mojo-hard-reset'
+;; Perform a hard reset, clearing all data.
+;; `mojo-package-install-and-launch'
+;; Package, install, and launch the current app.
+;; `mojo-package-install-and-inspect'
+;; Package, install, and launch the current app for inspection.
+;; `mojo-target-device'
+;; Set the target to a USB device.
+;; `mojo-target-emulator'
+;; Set the target to the emulator.
+
+;;; Customizable Options:
+;;
+;; Below are customizable option list:
+;;
+;; `mojo-sdk-directory'
+;; Path to where the mojo SDK is.
+;; default = (case system-type
+;; ((windows-nt) "c:/progra~1/palm/sdk")
+;; ((darwin) "/opt/PalmSDK/Current")
+;; (t ""))
+;; `mojo-project-directory'
+;; Directory where all your Mojo projects are located.
+;; default = ""
+;; `mojo-build-directory'
+;; Directory to build Mojo applications in.
+;; `mojo-debug'
+;; Run Mojo in debug mode. Assumed true while in such an early version.
+;; default = t
+
+#{files['CHANGELOG']}
+
+;;; Code:
+
+#{files['code.el']}
+
+;;; mojo ends here