diff --git a/.gitignore b/.gitignore index 9a18d21..677c465 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1 @@ .bundle -_blog -public/js/*.js -public/css/*.css -discussd/discuss.dirty -public/blog -public/proj -node_modules -public/s42/.htaccess -public/images/blog -public/f diff --git a/public/css/blog.css b/public/css/blog.css new file mode 100644 index 0000000..a8fa07c --- /dev/null +++ b/public/css/blog.css @@ -0,0 +1,179 @@ +body { margin: 0 + ; padding: 0 + } + +h1 { margin: 0 + ; padding: 0.2em + ; color: #9ab + } + +.center { text-align: center + ; font-size: 1.2em + } + +.hidden { display: none } + +#index { width: 80% + ; min-width: 300px + ; max-width: 800px + ; border: solid 1px #999 + ; -moz-border-radius: 10px + ; -webkit-border-radius: 10px + ; border-radius: 10px + ; background-color: #eee + ; margin: 1em auto + ; padding: 1em + ; font-size: 1.2em + ; line-height: 1.5em + ; list-style-type: none + } + +.date { float: right } + +#article, +article { width: 80% + ; min-width: 400px + ; max-width: 800px + ; margin: 0.6em auto + ; font-size: 1.2em + ; line-height: 1.4em + ; color: #222 + } + +#article h1, +article h1 { text-align: left + ; font-size: 2em + ; line-height: 1.2em + ; font-weight: normal + ; color: #222 + ; margin: 0 + ; padding-left: 0 + } + +#article h1 a, +article h1 a { color: #222 + ; text-decoration: underline + ; border-bottom: none + ; text-shadow: #ccc 1px 1px 5px + ; -webkit-transition: text-shadow 0.4s ease-in + } + +#article h1 a:hover, +article h1 a:hover { text-shadow: 1px 1px 6px #ffc + ; color: #000 + } + +#article h2, +article h2 { font-size: 1.8em + ; font-weight: normal + ; text-align: left + ; margin: 1em 0 + ; padding: 0 + ; color: #222 + } + +#article h3, +article h3 { font-size: 1.6em + ; font-weight: normal + } + +.time, +time { color: #444 + ; font-size: 1.2em + } + +.permalink { font-size: 1em } + +.gist { font-size: 0.8em } + +/* show discussion */ +#sd-container { margin: 3em 0 } + +input[type=submit], +#sd { border: solid 1px #999 + ; border-right-color: #333 + ; border-bottom-color: #333 + ; padding: 0.4em 1em + ; color: #444 + ; background-color: #ececec + ; -moz-border-radius: 5px + ; -webkit-border-radius: 5px + ; border-radius: 5px + ; text-decoration: none + ; margin: 0 2px 2px 0 + } + +input[type=submit]:active, +#sd:active { margin: 2px 0 0 2px + ; color: #000 + ; background-color: #ffc + } + +#comment-stuff { display: none + ; color: #efefef + ; margin: 0 + ; padding: 2em 0 + } + +#comments-spinner { text-align: center } + +#comments { width: 70% + ; max-width: 600px + ; margin: 0 auto + } + +.comment { color: #555 + ; border-top: solid 2px #ccc + ; padding-bottom: 2em + ; margin-bottom: 2em + } + +.comment big { font-size: 2em + ; font-family: Verdana, sans-serif + } + +#comment-form { width: 400px + ; margin: 2em auto 0 + } + +input[type=text], +textarea { font-size: 1.4em + ; color: #333 + ; width: 100% + ; padding: 0.2em + ; border: solid 1px #999 + ; -moz-border-radius: 5px + ; -webkit-border-radius: 5px + ; border-radius: 5px + ; font-family: verdana, sans-serif + } + +input:focus[type=text], +textarea:focus { border: solid 1px #333 } + +textarea { height: 100px } + +input[type=submit] { font-size: 1.1em + ; cursor: pointer + } + +pre { background-color: #eeeef3 + ; margin: 0.5em 1em 1em + ; padding: 0.5em + ; border: dashed 1px #ccc + } + +footer { margin: 0 auto + ; padding: 0.2em 0 + ; border-top: solid 1px #ddd + ; clear: both + ; width: 80% + } + +footer p { margin: 0.5em } + +footer a { border-bottom: none + ; color: #25c + ; font-size: 1.2em + ; text-decoration: none + } diff --git a/public/css/ie6.css b/public/css/ie6.css new file mode 100644 index 0000000..dc74a5d --- /dev/null +++ b/public/css/ie6.css @@ -0,0 +1,7 @@ +ul { behavior: none + ; padding-bottom: 25px + } + +img { behavior: url(../js/iepngfix.htc) + ; behavior: url(../../js/iepngfix.htc) + } diff --git a/public/css/ie7.css b/public/css/ie7.css new file mode 100644 index 0000000..c2a3ff0 --- /dev/null +++ b/public/css/ie7.css @@ -0,0 +1 @@ +ul#projects li { list-style-type: none } diff --git a/public/css/mobile.css b/public/css/mobile.css new file mode 100644 index 0000000..0c94472 --- /dev/null +++ b/public/css/mobile.css @@ -0,0 +1,140 @@ +/* phones and iPad */ + +@media only screen and (orientation: portrait) and (min-device-width: 768px) and (max-device-width: 1024px), + only screen and (min-device-width: 320px) and (max-device-width: 480px), + only screen and (max-device-width: 800px) +{ + ul.nav { padding: 0.5em + ; width: 60% + ; max-width: 600px + } + + ul.nav li { display: block + ; font-size: 1.5em + ; line-height: 1.8em + } + + ul.nav li:after { content: '' } +} + +/* phones */ +@media only screen and (min-device-width: 320px) and (max-device-width: 480px), + handheld and (max-device-width: 800px) +{ + /* common */ + + h1 { font-size: 2em + ; margin-top: 0.5em + } + h2 { font-size: 1.3em; line-height: 1.2em } + + .navbar { font-size: 0.9em } + .navbar { width: 32% } + #breadcrumbs { margin-left: 5px } + + #show-posts { margin-top: 1em + ; font-size: 0.8em + } + + #forkme { display: none } + + ul.nav { width: 80% } + + ul.nav li { font-size: 1.4em + ; line-height: 1.6em + } + + td { font-size: 1em + ; line-height: 1.1em + } + + img { max-width: 100% } + + #index { width: 90% + ; min-width: 200px + ; margin: 0.3em auto 1em + ; padding: 0.5em + ; font-size: 1em + } + + #index li > span.date { display: block + ; float: none + ; color: #666 + ; font-size: 0.8em + } + + #article h1, + article h1 { font-size: 1.6em + ; line-height: 1.2em + ; margin-top: 0 + } + + article h2 { font-size: 1.4em } + + #article, + article { min-width: 310px + ; margin: 0 + ; padding: 0.6em 0.4em + ; font-size: 0.8em + } + + .time, + time { font-size: 1.0em } + + pre, .gist { font-size: 0.8em } + + #comment-stuff { padding: 0 + ; margin-top: 2em + } + + #comments { width: 100% } + + #comment-form { width: 90% + ; margin: 0 auto + } + + input[type=text], + textarea { font-size: 1.2em + ; width: 95% + } + + input[type=submit] { font-size: 1em } + + /* proj */ + #info { width: 70% + ; padding: 0 1em + } + + #info > div { clear: left + ; width: 100% + ; max-width: 100% + ; padding: 0.5em 0.2em 1em + ; border-left: none + ; font-size: 1em + } + + #stats { font-size: 1em; margin-bottom: 0.5em } + + footer { margin: 0 + ; padding: 0.5em 0 + ; font-size: 1em + ; width: 100% + } + +} + +/* landscape */ + +@media only screen and (orientation: landscape) and (min-device-width: 768px) and (max-device-width: 1024px), + only screen and (orientation: landscape) and (min-device-width: 320px) and (max-device-width: 480px), + handheld and (orientation: landscape) and (max-device-width: 800px) +{ + body { font-size: 0.8em } +} + + +/* iPad portrait */ +@media only screen and (orientation: portrait) and (min-device-width: 768px) and (max-device-width: 1024px) +{ + article > header > h1 { font-size: 1.8em } +} diff --git a/public/css/projects.css b/public/css/projects.css new file mode 100644 index 0000000..0943667 --- /dev/null +++ b/public/css/projects.css @@ -0,0 +1,51 @@ +td { font-size: 1.5em + ; line-height: 1.6em + } + +td:nth-child(2) { padding: 0 10px } + +.highlight { font-size: 1.2em } + +#stats a { text-decoration: none } + +#info { text-align: center + ; margin: 1em auto + ; padding: 1em + ; border: solid 1px #ccc + ; width: 90% + ; max-width: 950px + ; background-color: #fff + ; -moz-border-radius: 20px + ; -webkit-border-radius: 20px + ; border-radius: 20px + ; behavior: url(../js/border-radius.htc) + ; behavior: url(../../js/border-radius.htc) + } + +h4 { margin: 0.5em 0 0.7em } + +#info > div { text-align: center + ; font-size: 1.3em + ; width: 31% + ; max-width: 400px + ; float: left + ; display: inline + ; padding: 0.5em 0.2em + ; border-left: dashed 1px #aaa + } + +#info > div:first-child { border-left: none } + +#info ul { list-style-type: none + ; text-align: center + ; padding: 0 + ; margin: 0 + } + +#info li { padding: 0.2em 0 + ; margin: 0 + } + +#info > br.clear { clear: both } + +#contributors-box a { line-height: 1.8em } diff --git a/public/css/style.css b/public/css/style.css new file mode 100644 index 0000000..9133570 --- /dev/null +++ b/public/css/style.css @@ -0,0 +1,92 @@ +TODO: include mobile.css here, just one set of styles + +body { background-color: #f7f7f7 + ; color: #222 + ; font-family: 'Helvetica Neue', Verdana, sans-serif + } + +h1 { text-align: center + ; font-size: 2em + ; font-weight: normal + ; margin: 0.8em 0 0.4em + ; padding: 0 + } + +h2 { text-align: center + ; font-size: 1.7em + ; line-height: 1.1em + ; font-weight: normal + ; margin: 0.2em 0 1em + ; padding: 0 + } + +a { color: #0E539C } + +a.img { border: none } + +.navbar { display: inline-block + ; width: 33% + ; font-size: 1.5em + ; line-height: 1.8em + ; margin: 0 + ; padding: 0 + } + +.navbar a { text-shadow: none } + +#breadcrumbs a { color: #222 } +#title { text-align: center } +#archive { text-align: right } + +#forkme { position: absolute + ; top: 0 + ; right: 0 + ; border: none + } + +ul.nav { text-align: center + ; max-width: 400px + ; margin: 0 auto + ; padding: 1em + ; border: solid 1px #ccc + ; background-color: #fff + ; -moz-border-radius: 20px + ; -webkit-border-radius: 20px + ; border-radius: 20px + ; behavior: url(js/border-radius.htc) + ; behavior: url(../js/border-radius.htc) + } + +ul.nav li { display: block + ; font-size: 1.6em + ; line-height: 1.8em + ; margin: 0 + ; padding: 0 + } + +ul.nav li a { padding: 5px + ; text-decoration: none + ; border-bottom: solid 1px #fff + ; text-shadow: #ccc 2px 2px 3px + } +ul.nav li a:visited { color: #227 } + +ul.nav li a:hover, +ul.nav li a:active { text-shadow: #cca 2px 2px 3px + ; border-bottom: solid 1px #aaa + } + +ul.nav li a:active { text-shadow: none } + +footer { text-align: center + ; font-size: 1.2em + ; margin: 1em + } + +footer a { border-bottom: none } + +#promote-js { margin-top: 3em + ; text-align: center + } + +#promote-js img { border: none } diff --git a/public/css/typocode.css b/public/css/typocode.css new file mode 100644 index 0000000..0f2a043 --- /dev/null +++ b/public/css/typocode.css @@ -0,0 +1,77 @@ +/* Syntax highlighting */ +.typocode_ruby .normal {} +.typocode_ruby .comment { color: #005; font-style: italic; } +.typocode_ruby .keyword { color: #A00; font-weight: bold; } +.typocode_ruby .method { color: #077; } +.typocode_ruby .class { color: #074; } +.typocode_ruby .module { color: #050; } +.typocode_ruby .punct { color: #447; font-weight: bold; } +.typocode_ruby .symbol { color: #099; } +.typocode_ruby .string { color: #944; background: #FFE; } +.typocode_ruby .char { color: #F07; } +.typocode_ruby .ident { color: #004; } +.typocode_ruby .constant { color: #07F; } +.typocode_ruby .regex { color: #B66; background: #FEF; } +.typocode_ruby .number { color: #F99; } +.typocode_ruby .attribute { color: #7BB; } +.typocode_ruby .global { color: #7FB; } +.typocode_ruby .expr { color: #227; } +.typocode_ruby .escape { color: #277; } +.typocode_xml .normal {} +.typocode_xml .namespace { color: #B66; font-weight: bold; } +.typocode_xml .tag { color: #F88; } +.typocode_xml .comment { color: #005; font-style: italic; } +.typocode_xml .punct { color: #447; font-weight: bold; } +.typocode_xml .string { color: #944; } +.typocode_xml .number { color: #F99; } +.typocode_xml .attribute { color: #BB7; } +.typocode_yaml .normal {} +.typocode_yaml .document { font-weight: bold; color: #07F; } +.typocode_yaml .type { font-weight: bold; color: #05C; } +.typocode_yaml .key { color: #F88; } +.typocode_yaml .comment { color: #005; font-style: italic; } +.typocode_yaml .punct { color: #447; font-weight: bold; } +.typocode_yaml .string { color: #944; } +.typocode_yaml .number { color: #F99; } +.typocode_yaml .time { color: #F99; } +.typocode_yaml .date { color: #F99; } +.typocode_yaml .ref { color: #944; } +.typocode_yaml .anchor { color: #944; } + +.typocode { + background-color:#eee; + padding:2px; + margin:5px; + margin-left:1em; + margin-bottom:1em; +} + +.typocode pre { + padding: 0px; + margin: 0px; + border: none; + background: transparent; + font-family: monospace; + overflow:auto; +} + + +.typocode .lineno { + text-align: right; + /* color: #B00;*/ + font-family: monospace; + padding-right: 1em; +} + +code { + font-family: "Andale Mono", "Lucida Typewriter", "Bitstream Vera Sans Mono", fixed-width, monospace; + font-size: 0.9em; +} + +pre { + background: #555; + border: 1px dashed #5189ea; + margin-bottom: 30px; + margin-top: -10px; + padding: 5px; +} diff --git a/public/f/Carmina Burana - O Fortuna.m4r b/public/f/Carmina Burana - O Fortuna.m4r new file mode 100644 index 0000000..f167192 Binary files /dev/null and b/public/f/Carmina Burana - O Fortuna.m4r differ diff --git a/public/f/Carmina Burana - O Fortuna.mp3 b/public/f/Carmina Burana - O Fortuna.mp3 new file mode 100644 index 0000000..5303c9a Binary files /dev/null and b/public/f/Carmina Burana - O Fortuna.mp3 differ diff --git a/public/f/IntelligentMigrationSnippets-0.1.dmg b/public/f/IntelligentMigrationSnippets-0.1.dmg new file mode 100644 index 0000000..a8597fc Binary files /dev/null and b/public/f/IntelligentMigrationSnippets-0.1.dmg differ diff --git a/public/f/SJRailsBundle-0.2.dmg b/public/f/SJRailsBundle-0.2.dmg new file mode 100644 index 0000000..b324194 Binary files /dev/null and b/public/f/SJRailsBundle-0.2.dmg differ diff --git a/public/f/assert_snippets.zip b/public/f/assert_snippets.zip new file mode 100644 index 0000000..539758d Binary files /dev/null and b/public/f/assert_snippets.zip differ diff --git a/public/f/cheat.el b/public/f/cheat.el new file mode 100644 index 0000000..bfa872c --- /dev/null +++ b/public/f/cheat.el @@ -0,0 +1,260 @@ +;; cheat.el +;; Time-stamp: <2007-08-22 10:00:04 sjs> +;; +;; Copyright (c) 2007 Sami Samhuri +;; +;; See http://sami.samhuri.net/2007/08/10/cheat-from-emacs for updates. +;; +;; License +;; +;; 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; either version 2 +;; of the License, or (at your option) any later version. +;; +;; 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. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program; if not, write to the Free Software +;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +;; +;; +;; Provide a handy interface to cheat. +;; See http://cheat.errtheblog.com for details on cheat itself. +;; +;; sjs 2007.08.21 +;; * Cache the list of cheat sheets, update it once a day (configurable). +;; * Strictly complete cheat sheet names. + +(defvar *cheat-host* "cheat.errtheblog.com") +(defvar *cheat-port* "80") +(defvar *cheat-uri* (concat *cheat-host* ":" *cheat-port*)) + +(defvar *cheat-directory* "~/.cheat") +(defvar *cheat-sheets-cache-file* (concat *cheat-directory* "/sheets")) + +(defvar *cheat-last-sheet* nil + "Name of the most recently viewed cheat sheet.") + +(defvar *cheat-sheet-history* nil + "List of the most recently viewed cheat sheets.") + +(defconst +seconds-per-day+ 86400) + +(defvar *cheat-cache-ttl* +seconds-per-day+ + "The minimum age of a stale cache file, in seconds.") + + +;;; interactive functions + +(defun cheat (name &optional silent) + "Show the specified cheat sheet. + +If SILENT is non-nil then do not print any output, but return it +as a string instead." + (interactive (list (cheat-read-sheet-name))) + (if silent + (cheat-command-silent name) + (cheat-command name))) + +(defun cheat-sheets () + "List all cheat sheets." + (interactive) + (cheat-command "sheets")) + +(defun cheat-recent () + "Show recently added cheat sheets." + (interactive) + (cheat-command "recent")) + +(defun cheat-clear-cache () + "Clear the local cheat cache, located in ~/.cheat." + (interactive) + (cheat-command "--clear-cache") + (make-directory *cheat-directory*)) + +(defun cheat-versions (name) + "Version history of the specified cheat sheet." + (interactive (list (cheat-read-sheet-name))) + (cheat-command name "--versions")) + +(defun cheat-diff (name version) + "Show the diff between the given version and the current version of the named cheat. +If VERSION is of the form m:n then show the diff between versions m and n." + (interactive (list (cheat-read-sheet-name) + (read-string "Cheat version(s): "))) + (cheat-command name "--diff" version)) + +(defun cheat-add-current-buffer (name) + "Add a new cheat with the specified name and the current buffer as the body." + (interactive "sCheat name: \n") + (post-cheat name (buffer-string) t) + (if (interactive-p) + (print (concat "Cheat added (" name ")")))) + +(defun cheat-edit (name) + "Fetch the named cheat and open a buffer containing its body. +The cheat can be saved with `cheat-save-current-buffer'." + (interactive (list (cheat-read-sheet-name))) + (cheat-clear-cache name) ; make sure we're working with the latest version + (switch-to-buffer (get-buffer-create (cheat->buffer name))) + (insert (cheat-body name)) + (if (interactive-p) + (print "Run `cheat-save-current-buffer' when you're done editing."))) + +(defun cheat-save-current-buffer () + "Save the current buffer using the buffer name for the title and the contents as the body." + (interactive) + (let ((name (buffer->cheat (buffer-name (current-buffer))))) + (post-cheat name (buffer-string)) + ;; TODO check for errors and kill the buffer on success + (if (interactive-p) + (print (concat "Cheat saved (" name ")"))) + (cheat-clear-cache name) + (cheat name))) + + +;;; helpers + +;; 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 cheat-command (&rest rest) + "Run the cheat command with the given arguments, display the output." + (interactive "sArguments for cheat: \n") + (shell-command (concat "cheat " (string-join " " rest)))) + +(defun cheat-command-to-string (&rest rest) + "Run the cheat command with the given arguments and return the output as a string. Display nothing." + (shell-command-to-string (concat "cheat " (string-join " " rest)))) + +(defalias 'cheat-command-silent 'cheat-command-to-string) + +(defun cheat-read-sheet-name (&optional prompt) + "Get the name of an existing cheat sheet, prompting with completion and history. + +The name of the sheet read is stored in *cheat-last-sheet* unless it was blank." + (let* ((default (when (blank prompt) *cheat-last-sheet*)) + (prompt (or prompt + (if (not (blank default)) + (concat "Cheat name (default: " default "): ") + "Cheat name: "))) + (name (completing-read prompt + (cheat-sheets-list t) + nil + t + nil + '*cheat-sheet-history* + default))) + (when (not (blank name)) + (setq *cheat-last-sheet* name)) + name)) + +(defun cheat-sheets-list (&optional fetch-if-missing-or-stale) + "Get a list of all cheat sheets. + +Return the cached list in *cheat-sheets-cache-file* if it's +readable and `cheat-cache-stale-p' returns nil. + +When there is no cache or a stale cache, and +FETCH-IF-MISSING-OR-STALE is non-nil, cache the list and then +return it. + +Otherwise return nil." + (cond ((and (file-readable-p *cheat-sheets-cache-file*) + (not (cheat-cache-stale-p))) + (save-excursion + (let* ((buffer (find-file *cheat-sheets-cache-file*)) + (sheets (split-string (buffer-string)))) + (kill-buffer buffer) + sheets))) + (fetch-if-missing-or-stale + (cheat-cache-list) + (cheat-sheets-list)) + (t nil))) + +(defun cheat-fetch-list () + "Fetch a fresh list of all cheat sheets." + (nthcdr 3 (split-string (cheat-command-to-string "sheets")))) + +(defun cheat-cache-list () + "Cache the list of cheat sheets in *cheat-sheets-cache-file*. Return the list." + (when (not (file-exists-p *cheat-directory*)) + (make-directory *cheat-directory*)) + (save-excursion + (let ((buffer (find-file *cheat-sheets-cache-file*)) + (sheets (cheat-fetch-list))) + (insert (string-join "\n" sheets)) + (basic-save-buffer) + (kill-buffer buffer) + sheets))) + +(defun cheat-cache-stale-p () + "Non-nil if the cache in *cheat-sheets-cache-file* is more than *cheat-cache-ttl* seconds old.q + +If the cache file does not exist then it is considered stale. + +Also see `cheat-cache-sheets'." + (or (null (file-exists-p *cheat-sheets-cache-file*)) + (let* ((now (float-time (current-time))) + (last-mod (float-time (sixth (file-attributes *cheat-sheets-cache-file*)))) + (age (- now last-mod))) + (> age *cheat-cache-ttl*)))) + +(defun cheat-body (name) + "Call out to Ruby to load the YAML and return just the body." + (shell-command-to-string + (concat "ruby -ryaml -e '" + "puts YAML.load_file(File.expand_path(\"~/.cheat/" + name ".yml\")).to_a[0][-1]'"))) + +(defun url-http-post (url args) + "Send ARGS to URL as a POST request." + (let ((url-request-method "POST") + (url-request-extra-headers + '(("Content-Type" . "application/x-www-form-urlencoded"))) + (url-request-data + (concat (mapconcat (lambda (arg) + (concat (url-hexify-string (car arg)) + "=" + (url-hexify-string (cdr arg)))) + args + "&") + "\r\n"))) + ;; `kill-url-buffer' to discard the result + ;; `switch-to-url-buffer' to view the results (debugging). + (url-retrieve url 'kill-url-buffer))) + +(defun kill-url-buffer (status) + "Kill the buffer returned by `url-retrieve'." + (kill-buffer (current-buffer))) + +(defun switch-to-url-buffer (status) + "Switch to the buffer returned by `url-retreive'. + The buffer contains the raw HTTP response sent by the server." + (switch-to-buffer (current-buffer))) + +(defun post-cheat (title body &optional new) + (let ((uri (concat "http://" *cheat-uri* "/w/" (if new "" title)))) + (url-http-post uri `(("sheet_title" . ,title) + ("sheet_body" . ,body) + ("from_gem" . "1"))))) + +(defun buffer->cheat (name) + (substring name 7 (- (length name) 1))) + +(defun cheat->buffer (name) + (concat "*cheat-" name "*")) + +(provide 'cheat) diff --git a/public/f/download.png b/public/f/download.png new file mode 100644 index 0000000..3987548 Binary files /dev/null and b/public/f/download.png differ diff --git a/public/f/ember-structure.png b/public/f/ember-structure.png new file mode 100644 index 0000000..dca1b59 Binary files /dev/null and b/public/f/ember-structure.png differ diff --git a/public/f/feed-icon-48x48.png b/public/f/feed-icon-48x48.png new file mode 100644 index 0000000..12ba523 Binary files /dev/null and b/public/f/feed-icon-48x48.png differ diff --git a/public/f/feed-icon32x32.png b/public/f/feed-icon32x32.png new file mode 100644 index 0000000..b08f650 --- /dev/null +++ b/public/f/feed-icon32x32.png @@ -0,0 +1,256 @@ + + +samhuri.net + + + + + + + + + + + + + + + + +

samhuri.net + + + + + + + + diff --git a/public/f/german_keys.txt b/public/f/german_keys.txt new file mode 100644 index 0000000..b429132 --- /dev/null +++ b/public/f/german_keys.txt @@ -0,0 +1,35 @@ +DVORAK +------ +§1234567890[] +±!@#$%^&*(){} + +',.pyfgcrl/= +"<>PYFGCRL?+ + +aoeuidhtns-\ +AOEUIDHTNS_| + +`;qjkxbmwvz +~:QJKXBMWVZ + + +QWERTY +------ +§12344567890-= +±!@#$%^&*()_+ + +qwertyuiop[] +QWERTYUIOP{} + +asdfghjkl;'\ +ASDFGHJKL:"| + +`zxcvbnm,./ +~ZXCVBNM<>? + + +QWERTZ (German) +--------------- +^1234567890ß´ +°!"§$%&/()=?` + diff --git a/public/f/gtkpod-aac-fix.sh b/public/f/gtkpod-aac-fix.sh new file mode 100644 index 0000000..36ea9d7 --- /dev/null +++ b/public/f/gtkpod-aac-fix.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +mkdir /tmp/gtkpod-fix +cd /tmp/gtkpod-fix +wget http://ftp.uni-kl.de/debian-multimedia/pool/main/libm/libmpeg4ip/libmp4v2-0_1.5.0.1-0.3_amd64.deb +wget http://ftp.uni-kl.de/debian-multimedia/pool/main/libm/libmpeg4ip/libmp4v2-dev_1.5.0.1-0.3_amd64.deb +wget http://ftp.uni-kl.de/debian-multimedia/pool/main/libm/libmpeg4ip/libmpeg4ip-0_1.5.0.1-0.3_amd64.deb +wget http://ftp.uni-kl.de/debian-multimedia/pool/main/libm/libmpeg4ip/libmpeg4ip-dev_1.5.0.1-0.3_amd64.deb +for f in *.deb; do sudo gdebi -n "$f"; done +svn co https://gtkpod.svn.sourceforge.net/svnroot/gtkpod/gtkpod/trunk gtkpod +cd gtkpod +./autogen.sh --with-mp4v2 && make && sudo make install +cd +rm -rf /tmp/gtkpod-fix diff --git a/public/f/ims-demo.mov b/public/f/ims-demo.mov new file mode 100644 index 0000000..b12ed37 Binary files /dev/null and b/public/f/ims-demo.mov differ diff --git a/public/f/island.css b/public/f/island.css new file mode 100644 index 0000000..f1c16a0 --- /dev/null +++ b/public/f/island.css @@ -0,0 +1,438 @@ +/* Island screen styles */ + +body { + font: 1em/1.15em "Hoefler Text", Georgia, "Adobe Garamond Pro", Palatino, "Times New Roman", serif; + color: #eee; + background: #000; + margin: 5px; +} + +#leftcolumn { + width: 23%; + float: right; + margin: 0 10px 0 20px; + text-align: right; +} + +#header { +/* text-align: center;*/ +} + +#content { +/* padding-top: 50px;*/ + width: 72%; + float: left; +} + +#main { +} + +#search { + padding: 3px; +} + +#sidebar { +} + +#footer { + text-align: center; +/* background: #333;*/ + border-top: 1px solid #666; + padding-bottom: 5px; + clear: both; +} + +#footer a { border: none; } +#footer img { border: none; } + +/*+-------------------------------------------+ +| LINKS | ++-------------------------------------------+*/ + +a { + color: #5189ea; + text-decoration: none; + border-bottom: 1px dotted #3179ca; + padding-top: 2px; +} + +a:hover { + background: #ffa; + color: #000; +} + +/*+-------------------------------------------+ +| UTILITY | ++-------------------------------------------+*/ + +h1, h2, h3, h4, h5, h6 { + margin: 0; + padding: 0; +} + +form, div { padding: 0; margin: 0; } +p { margin: 0 0 1em 0; padding: 0; } +img { + border: 0; + padding: 5px; +} + +/*+-------------------------------------------+ +| HEADER | ++-------------------------------------------+*/ + +#header h1 { + margin-top: 30px; + font-size: 5em; +} + +#header h1 a { + color: #ffa; + background: inherit; + text-decoration: none; + border-bottom: none; +} + +#header h1 a:hover { + background: #000; +} + +#header h2 { + color: #555; + padding-top: .2em; + font-size: 1.2em; + letter-spacing: 2px; +} + +/*+-------------------------------------------+ +| SEARCH | ++-------------------------------------------+*/ + +#sform { + padding-top: 15px; +} + +#search-results { + text-align: left; + padding-bottom: 5px; + border-bottom: 1px dotted #666; +} + +/*+-------------------------------------------+ +| POST | ++-------------------------------------------+*/ + +.post { + padding: 10px 0 10px 0; + margin: 5px 0 15px 5px; +/* clear: left;*/ +} + +.post h2 { + font-size: 140%; + line-height: 1.4em; + margin: 5px 0; +/* padding-top: 15px;*/ +} + +.post h3 { + margin: 5px 0; +} + +.post h2 a { + color: #fff; + text-decoration: none; + border: none; +} + +.post h2 a:before { + content: '# '; +} + +.post h2 a:hover { + color: #ffa; + background: #000; +} + +.post p { + margin-left: 5px; +} + +p.meta { + font-size: 75%; + text-align: right; + margin-right: 15px; +/* border-right: 1px solid #999;*/ +/* float: left;*/ +/* width: 75px;*/ +} + +p.auth { + font-size: 85%; + font-weight: bold; + color: #999; +} + +/*+-------------------------------------------+ +| COMMENTS | ++-------------------------------------------+*/ + +.comment-list li, #preview { + background: #666; + padding: 5px; + margin-bottom: 8px; + color: #eee; + /*border-right: 20px solid #d3e0ea;*/ + min-height: 60px; +} + +.gravatar { + float: right; + padding: 0 5px; +} + +/*+-------------------------------------------+ +| SIDEBAR | ++-------------------------------------------+*/ + +#sidebar h3 { + font-size: 110%; + line-height: 1.5em; + color: #ff9d47; + margin-top: 15px; + margin-bottom: 2px; + text-transform: lowercase; +} + +#sidebar h3:before { + content: "<< "; + font-size: small; +} +#sidebar h3:after { + content: " >>"; + font-size: small; +} + +#sidebar h3 a { + color: #ff9d47; + border-bottom: 1px dotted #ff9d47; +} + +#sidebar h3 a:hover { + color: #000; + background: #ffa; +} + +#sidebar ul { + text-align: right; + margin: 0; + padding: 0; + list-style-type: none; +} + +#sidebar ul li { +/* background: url(http://web.archive.org/web/20060321041209/http://sami.samhuri.net/images/theme/small_arrow.png) 45% no-repeat;*/ +/* padding-left: 12px;*/ +} + +#sidebar a.feed, +#sidebar a.feed:link, +#sidebar a.feed:hover { + display: inline; + border: none; + background: #000; +} + +/*+-------------------------------------------+ +| FOOTER | ++-------------------------------------------+*/ + +#footer ul { + margin: 0; + padding: 10px 0 0 0; + list-style: none; +} + +#footer ul li { + display: inline; + margin: 0; + padding: 0 5px 0 0; + font-size: 100%; +} + +/*+-------------------------------------------+ +| FORMS | ++-------------------------------------------+*/ + +input, textarea, select { + border: 1px solid #8ab3d1; + background: #f9f9f9; +} + +.frm-tbl td { vertical-align: top; padding: 5px; } +td#frm-btns { text-align: right; } + +#comment_body { height: 220px; } +#form-submit-button { background: #d3e0ea; } + +/*+-------------------------------------------+ +| MISC | ++-------------------------------------------+*/ + +.powered { border: none; } +.powered img { margin-top: 20px; } +.pullquote { + width: 30%; + float:right; + font: 150%/1.5em Times, Helvetica, "Times New Roman", serif; + color: #666; + margin:10px; + background: url(http://web.archive.org/web/20060321041209/http://sami.samhuri.net/images/theme/q-close.gif) no-repeat 70% 100% !important; + background /**/:url(); /* removing quote graphic in IE5+ */ + padding: 0 25px 5px 0; +} +.pullquote:first-letter { + background: url(http://web.archive.org/web/20060321041209/http://sami.samhuri.net/images/theme/q-open.gif) no-repeat left top !important; + padding:5px 2px 10px 35px!important; + padding /**/:0px; /* resetting padding in IE5+ */ + background /**/: url(); /* removing quote graphic in IE5+ */ +} + +.light-bg { background: #666; padding: 2px; } +.clearfix:after { + content: "."; + display: block; + height: 0; + clear: both; + visibility: hidden; +} + +#errors { + color: red; +} + +.admintools { + float: right; + background-color: #fff; + font-size: smaller; + padding: 0 2px; + margin: 0 1px; + color: #ccc; + border: 1px solid #ccc; +} + +.admintools:hover { + color: #c00; + border: 1px solid #c00; +} + +a[name] { border: none; } + +/* Hides from IE-mac \*/ +* html .clearfix {height: 1%;} +/* End hide from IE-mac */ + +#categories li em, #archives li em +{ + color: #98B4D1; + font-size: smaller; +} + +/* Syntax highlighting */ +.typocode_ruby .normal {} +.typocode_ruby .comment { color: #005; font-style: italic; } +.typocode_ruby .keyword { color: #A00; font-weight: bold; } +.typocode_ruby .method { color: #077; } +.typocode_ruby .class { color: #074; } +.typocode_ruby .module { color: #050; } +.typocode_ruby .punct { color: #447; font-weight: bold; } +.typocode_ruby .symbol { color: #099; } +.typocode_ruby .string { color: #944; background: #FFE; } +.typocode_ruby .char { color: #F07; } +.typocode_ruby .ident { color: #004; } +.typocode_ruby .constant { color: #07F; } +.typocode_ruby .regex { color: #B66; background: #FEF; } +.typocode_ruby .number { color: #F99; } +.typocode_ruby .attribute { color: #7BB; } +.typocode_ruby .global { color: #7FB; } +.typocode_ruby .expr { color: #227; } +.typocode_ruby .escape { color: #277; } +.typocode_xml .normal {} +.typocode_xml .namespace { color: #B66; font-weight: bold; } +.typocode_xml .tag { color: #F88; } +.typocode_xml .comment { color: #005; font-style: italic; } +.typocode_xml .punct { color: #447; font-weight: bold; } +.typocode_xml .string { color: #944; } +.typocode_xml .number { color: #F99; } +.typocode_xml .attribute { color: #BB7; } +.typocode_yaml .normal {} +.typocode_yaml .document { font-weight: bold; color: #07F; } +.typocode_yaml .type { font-weight: bold; color: #05C; } +.typocode_yaml .key { color: #F88; } +.typocode_yaml .comment { color: #005; font-style: italic; } +.typocode_yaml .punct { color: #447; font-weight: bold; } +.typocode_yaml .string { color: #944; } +.typocode_yaml .number { color: #F99; } +.typocode_yaml .time { color: #F99; } +.typocode_yaml .date { color: #F99; } +.typocode_yaml .ref { color: #944; } +.typocode_yaml .anchor { color: #944; } + +.typocode { + background-color:#eee; + padding:2px; + margin:5px; + margin-left:1em; + margin-bottom:1em; +} + +.typocode pre { + padding: 0px; + margin: 0px; + border: none; + background: transparent; + font-family: monospace; + overflow:auto; +} + + +.typocode .lineno { + text-align: right; + /* color: #B00;*/ + font-family: monospace; + padding-right: 1em; +} + +code { + font-family: "Andale Mono", "Lucida Typewriter", "Bitstream Vera Sans Mono", fixed-width, monospace; +/* font-size: 120%;*/ +} + +pre { + background: #555; + border: 1px dashed #5189ea; + margin-bottom: 30px; + margin-top: -10px; + padding: 5px; +} + +/*+-------------------------------------------+ +| TAGLIST | ++-------------------------------------------+*/ + +ul#taglist li { + display: inline; + line-height: 3em; +} + +.tags .tags0 { font-size: 1.0em; } +.tags .tags1 { font-size: 1.2em; } +.tags .tags2 { font-size: 1.4em; } +.tags .tags3 { font-size: 1.6em; } +.tags .tags4 { font-size: 1.8em; } +.tags .tags5 { font-size: 2.0em; } +.tags .tags6 { font-size: 2.2em; } +.tags .tags7 { font-size: 2.4em; } +.tags .tags8 { font-size: 2.6em; } +.tags .tags9 { font-size: 2.8em; } +.tags .tags10 { font-size: 3.0em; } diff --git a/public/f/menu.png b/public/f/menu.png new file mode 100644 index 0000000..637fda6 Binary files /dev/null and b/public/f/menu.png differ diff --git a/public/f/mephisto_converters-typo-schema_version_61.patch b/public/f/mephisto_converters-typo-schema_version_61.patch new file mode 100644 index 0000000..50aa741 --- /dev/null +++ b/public/f/mephisto_converters-typo-schema_version_61.patch @@ -0,0 +1,81 @@ +diff -Nupr mephisto_converters/lib/converters/typo/article.rb mephisto_converters-schema_61/lib/converters/typo/article.rb +--- mephisto_converters/lib/converters/typo/article.rb 2006-11-23 10:57:06.000000000 -0800 ++++ mephisto_converters-schema_61/lib/converters/typo/article.rb 2007-05-26 01:54:19.000000000 -0700 +@@ -1,7 +1,8 @@ + module Typo + class Article < Content +- has_many :comments, :dependent => true, :order => "created_at ASC", :class_name => 'Typo::Comment' ++ has_many :comments, :dependent => :destroy, :order => "created_at ASC", :class_name => 'Typo::Comment' + has_and_belongs_to_many :tags, :class_name => 'Typo::Tag' +- has_and_belongs_to_many :categories, :class_name => 'Typo::Category' ++ has_many :categorizations, :class_name => 'Typo::Categorization' ++ has_many :categories, :class_name => 'Typo::Category', :through => :categorizations + end + end +diff -Nupr mephisto_converters/lib/converters/typo/categorization.rb mephisto_converters-schema_61/lib/converters/typo/categorization.rb +--- mephisto_converters/lib/converters/typo/categorization.rb 1969-12-31 16:00:00.000000000 -0800 ++++ mephisto_converters-schema_61/lib/converters/typo/categorization.rb 2007-05-26 01:44:19.000000000 -0700 +@@ -0,0 +1,7 @@ ++module Typo ++ class Categorization < ActiveRecord::Base ++ establish_connection configurations['typo'] ++ belongs_to :article, :class_name => 'Typo::Article' ++ belongs_to :category, :class_name => 'Typo::Category' ++ end ++end +diff -Nupr mephisto_converters/lib/converters/typo/category.rb mephisto_converters-schema_61/lib/converters/typo/category.rb +--- mephisto_converters/lib/converters/typo/category.rb 2006-11-23 10:57:06.000000000 -0800 ++++ mephisto_converters-schema_61/lib/converters/typo/category.rb 2007-05-26 01:42:02.000000000 -0700 +@@ -1,6 +1,7 @@ + module Typo + class Category < ActiveRecord::Base + establish_connection configurations['typo'] +- has_and_belongs_to_many :articles, :class_name => 'Typo::Article' ++ has_many :categorizations, :class_name => 'Typo::Categorization' ++ has_many :articles, :class_name => 'Typo::Article', :through => :categorizations + end + end +diff -Nupr mephisto_converters/lib/converters/typo/comment.rb mephisto_converters-schema_61/lib/converters/typo/comment.rb +--- mephisto_converters/lib/converters/typo/comment.rb 2006-11-23 10:57:06.000000000 -0800 ++++ mephisto_converters-schema_61/lib/converters/typo/comment.rb 2007-05-26 01:54:30.000000000 -0700 +@@ -1,5 +1,7 @@ + module Typo +- class Comment < Content +-# belongs_to :article, :foreign_key => 'parentid', :class_name => 'Typo::Comment' ++ class Comment < Feedback ++ establish_connection configurations['typo'] ++ belongs_to :article, :class_name => 'Typo::Article' ++ belongs_to :user, :class_name => 'Typo::User' + end + end +diff -Nupr mephisto_converters/lib/converters/typo/feedback.rb mephisto_converters-schema_61/lib/converters/typo/feedback.rb +--- mephisto_converters/lib/converters/typo/feedback.rb 1969-12-31 16:00:00.000000000 -0800 ++++ mephisto_converters-schema_61/lib/converters/typo/feedback.rb 2007-05-26 01:55:28.000000000 -0700 +@@ -0,0 +1,6 @@ ++module Typo ++ class Feedback < Content ++ set_table_name 'feedback' ++ establish_connection configurations['typo'] ++ end ++end +diff -Nupr mephisto_converters/lib/converters/typo.rb mephisto_converters-schema_61/lib/converters/typo.rb +--- mephisto_converters/lib/converters/typo.rb 2006-11-23 10:57:06.000000000 -0800 ++++ mephisto_converters-schema_61/lib/converters/typo.rb 2007-05-26 01:53:14.000000000 -0700 +@@ -1,8 +1,10 @@ + require 'converters/typo/content' + require 'converters/typo/page' + require 'converters/typo/article' ++require 'converters/typo/feedback' + require 'converters/typo/comment' + require 'converters/typo/category' ++require 'converters/typo/categorization' + require 'converters/typo/user' + require 'converters/typo/tag' + +@@ -77,4 +79,4 @@ class TypoConverter < BaseConverter + memo << (sections[::Section.permalink_for(cat.name)] || site.sections.create(:name => cat.name)).id + end + end +-end +\ No newline at end of file ++end diff --git a/public/f/note.html b/public/f/note.html new file mode 100644 index 0000000..d0870af --- /dev/null +++ b/public/f/note.html @@ -0,0 +1,29 @@ + + + + +

Notepad (type below, notes persist)

+

+ diff --git a/public/f/pacman-tones.zip b/public/f/pacman-tones.zip new file mode 100644 index 0000000..e06daa0 Binary files /dev/null and b/public/f/pacman-tones.zip differ diff --git a/public/f/pacman-tones/Pacman Dies.mp3 b/public/f/pacman-tones/Pacman Dies.mp3 new file mode 100644 index 0000000..28b7dac Binary files /dev/null and b/public/f/pacman-tones/Pacman Dies.mp3 differ diff --git a/public/f/pacman-tones/Pacman.mp3 b/public/f/pacman-tones/Pacman.mp3 new file mode 100644 index 0000000..4d9e68b Binary files /dev/null and b/public/f/pacman-tones/Pacman.mp3 differ diff --git a/public/f/spinner-blue.gif b/public/f/spinner-blue.gif new file mode 100644 index 0000000..409de02 Binary files /dev/null and b/public/f/spinner-blue.gif differ diff --git a/public/f/spinner.gif b/public/f/spinner.gif new file mode 100644 index 0000000..1ed786f Binary files /dev/null and b/public/f/spinner.gif differ diff --git a/public/f/tagify.el b/public/f/tagify.el new file mode 100644 index 0000000..624128e --- /dev/null +++ b/public/f/tagify.el @@ -0,0 +1,34 @@ +(defun wrap-region (left right) + "Wrap the region in arbitrary text, LEFT goes to the left and RIGHT goes to the right." + (interactive) + (let ((beg (region-beginning)) + (end (region-end))) + (goto-char beg) + (insert left) + (goto-char (+ end (length left))) + (insert right) + (goto-char (+ end (length left) (length right))))) + +(defun tagify-region-or-insert-self (arg) + "If there is a visible region, call `tagify-region-or-insert', otherwise +call `self-insert-command' passing it any prefix arg given." + (interactive "*P") + (if (and mark-active transient-mark-mode) + (call-interactively 'tagify-region-or-insert-tag) + (self-insert-command (prefix-numeric-value arg)))) + +(defun tagify-region-or-insert-tag (tag) + "If there is a visible region, wrap it in the given HTML/XML tag using +`wrap-region'. If any attributes are specified then they are only included +in the opening tag. + +Otherwise insert the opening and closing tags and position point between the two." + (interactive "*sTag (including attributes): \n") + (let* ((open (concat "<" tag ">")) + (close (concat ""))) + (if (and mark-active transient-mark-mode) + (wrap-region open close) + (insert (concat open close)) + (backward-char (length close))))) + +(provide 'tagify) \ No newline at end of file diff --git a/public/f/volume.rb b/public/f/volume.rb new file mode 100644 index 0000000..081fca0 --- /dev/null +++ b/public/f/volume.rb @@ -0,0 +1,21 @@ +#!/usr/bin/env ruby + +Ops = { + :+ => [lambda { |x| x + 5 > 100 ? 100 : x + 5 }, "increased"], + :- => [lambda { |x| x - 5 < 0 ? 0 : x - 5 } , "decreased"] +} + +def vol_op id, vol + [Ops[id].first.call(vol), Ops[id].last] +end + +op = ARGV.shift.to_sym +unless Ops.keys.include?(op) + raise "#{op} is not a valid argument. Expected one of: #{Ops.keys.join(', ')}." +else + `amixer get Master`.split("\n").grep(/%/).first =~ /\[(\d+)/ + vol = $1.to_i + new_vol, action = *vol_op(op, vol) + system("amixer set Master #{new_vol}%") + system("notify-send --urgency=low --icon=~/.icons/audio-volume-medium.gif --expire-time=500 \"Volume #{action}\" \"#{new_vol}%\"") +end diff --git a/public/f/wrap-region.el b/public/f/wrap-region.el new file mode 100644 index 0000000..a5a7b34 --- /dev/null +++ b/public/f/wrap-region.el @@ -0,0 +1,49 @@ +;; help out a TextMate junkie + +(defun wrap-region (left right beg end) + "Wrap the region in arbitrary text, LEFT goes to the left and RIGHT goes to the right." + (interactive) + (save-excursion + (goto-char beg) + (insert left) + (goto-char (+ end (length left))) + (insert right))) + +(defmacro wrap-region-with-function (left right) + "Returns a function which, when called, will interactively `wrap-region-or-insert' using LEFT and RIGHT." + `(lambda () (interactive) + (wrap-region-or-insert ,left ,right))) + +(defun wrap-region-with-tag-or-insert () + (interactive) + (if (and mark-active transient-mark-mode) + (call-interactively 'wrap-region-with-tag) + (insert "<"))) + +(defun wrap-region-with-tag (tag beg end) + "Wrap the region in the given HTML/XML tag using `wrap-region'. If any +attributes are specified then they are only included in the opening tag." + (interactive "*sTag (including attributes): \nr") + (let* ((elems (split-string tag " ")) + (tag-name (car elems)) + (right (concat ""))) + (if (= 1 (length elems)) + (wrap-region (concat "<" tag-name ">") right beg end) + (wrap-region (concat "<" tag ">") right beg end)))) + +(defun wrap-region-or-insert (left right) + "Wrap the region with `wrap-region' if an active region is marked, otherwise insert LEFT at point." + (interactive) + (if (and mark-active transient-mark-mode) + (wrap-region left right (region-beginning) (region-end)) + (insert left))) + +(global-set-key "'" (wrap-region-with-function "'" "'")) +(global-set-key "\"" (wrap-region-with-function "\"" "\"")) +(global-set-key "`" (wrap-region-with-function "`" "`")) +(global-set-key "(" (wrap-region-with-function "(" ")")) +(global-set-key "[" (wrap-region-with-function "[" "]")) +(global-set-key "{" (wrap-region-with-function "{" "}")) +(global-set-key "<" 'wrap-region-with-tag-or-insert) ;; I opted not to have a wrap-with-angle-brackets + +(provide 'wrap-region) diff --git a/public/f/zelda-tones.zip b/public/f/zelda-tones.zip new file mode 100644 index 0000000..155dd34 Binary files /dev/null and b/public/f/zelda-tones.zip differ diff --git a/public/f/zelda-tones/Zelda Achievement.mp3 b/public/f/zelda-tones/Zelda Achievement.mp3 new file mode 100644 index 0000000..9d66667 Binary files /dev/null and b/public/f/zelda-tones/Zelda Achievement.mp3 differ diff --git a/public/f/zelda-tones/Zelda Item.mp3 b/public/f/zelda-tones/Zelda Item.mp3 new file mode 100644 index 0000000..7dfd4eb Binary files /dev/null and b/public/f/zelda-tones/Zelda Item.mp3 differ diff --git a/public/f/zelda-tones/Zelda Overture.mp3 b/public/f/zelda-tones/Zelda Overture.mp3 new file mode 100644 index 0000000..929b0d6 Binary files /dev/null and b/public/f/zelda-tones/Zelda Overture.mp3 differ diff --git a/public/f/zelda-tones/Zelda Secret.mp3 b/public/f/zelda-tones/Zelda Secret.mp3 new file mode 100644 index 0000000..02cfcca Binary files /dev/null and b/public/f/zelda-tones/Zelda Secret.mp3 differ diff --git a/public/f/zelda-tones/Zelda Song of Storms.mp3 b/public/f/zelda-tones/Zelda Song of Storms.mp3 new file mode 100644 index 0000000..f331fa4 Binary files /dev/null and b/public/f/zelda-tones/Zelda Song of Storms.mp3 differ diff --git a/public/f/zelda-tones/Zelda Theme.mp3 b/public/f/zelda-tones/Zelda Theme.mp3 new file mode 100644 index 0000000..658ee3a Binary files /dev/null and b/public/f/zelda-tones/Zelda Theme.mp3 differ diff --git a/public/images/blog/ch1-Z-G-4.gif b/public/images/blog/ch1-Z-G-4.gif new file mode 100644 index 0000000..c8bada3 Binary files /dev/null and b/public/images/blog/ch1-Z-G-4.gif differ diff --git a/public/images/blog/download.png b/public/images/blog/download.png new file mode 100644 index 0000000..3987548 Binary files /dev/null and b/public/images/blog/download.png differ diff --git a/public/images/blog/keyboard.jpg b/public/images/blog/keyboard.jpg new file mode 100644 index 0000000..406541a Binary files /dev/null and b/public/images/blog/keyboard.jpg differ diff --git a/public/images/blog/menu.png b/public/images/blog/menu.png new file mode 100644 index 0000000..637fda6 Binary files /dev/null and b/public/images/blog/menu.png differ diff --git a/public/js/gitter.js b/public/js/gitter.js new file mode 100644 index 0000000..5bd2e95 --- /dev/null +++ b/public/js/gitter.js @@ -0,0 +1,766 @@ +/// gitter +/// http://github.com/samsonjs/gitter +/// @_sjs +/// +/// Copyright 2010 - 2012 Sami Samhuri +/// MIT License + +(function() { + "use strict" + + var global = (function() { return this || (1, eval)('this') }()) + , isBrowser = 'document' in global + , ie + + if (isBrowser) { + ie = (function() { + var undef + , v = 3 + , div = document.createElement('div') + , all = div.getElementsByTagName('i') + + while ( + div.innerHTML = '', + all[0] + ); + + return v > 4 ? v : undef + }()) + } + + var inherits + if ('create' in Object) { + // util.inherits from node + inherits = function(ctor, superCtor) { + ctor.super_ = superCtor + ctor.prototype = Object.create(superCtor.prototype, { + constructor: { + value: ctor, + enumerable: false + } + }) + } + } + else if ([].__proto__) { + inherits = function(ctor, superCtor) { + ctor.super_ = superCtor + ctor.prototype.__proto__ = superCtor.prototype + ctor.prototype.constructor = ctor + } + } + else { // ie8 + var __hasProp = Object.prototype.hasOwnProperty + inherits = function(child, parent) { + for (var key in parent) { + if (__hasProp.call(parent, key)) child[key] = parent[key] + } + function ctor() { this.constructor = child } + ctor.prototype = parent.prototype + child.prototype = new ctor + child.__super__ = parent.prototype + return child + } + } + + var api = { + // Blob + blob: function(user, repo, sha, cb) { + return new Blob(user, repo, sha, cb) + } + + // Branch + , branch: function(user, repo, name, cb) { + return new Branch(user, repo, name, cb) + } + + // Commit + , commit: function(user, repo, sha, cb) { + return new Commit(user, repo, sha, cb) + } + + // Download + , download: function(user, repo, id, cb) { + return new Download(user, repo, id, cb) + } + + // Issue + , issue: function(user, repo, id, cb) { + return new Issue(user, repo, id, cb) + } + + // Organization + , org: function(name, cb) { + return new Org(name, cb) + } + , members: function(name, cb) { + return new Org(name).fetchMembers(cb) + } + + // Repo + , repo: function(user, repo, cb) { + return new Repo(user, repo, cb) + } + , branches: function(user, repo, cb) { + return new Repo(user, repo).fetchBranches(cb) + } + , collaborators: function(user, repo, cb) { + return new Repo(user, repo).fetchCollaborators(cb) + } + , contributors: function(user, repo, cb) { + return new Repo(user, repo).fetchContributors(cb) + } + , downloads: function(user, repo, cb) { + return new Repo(user, repo).fetchDownloads(cb) + } + , forks: function(user, repo, cb) { + return new Repo(user, repo).fetchForks(cb) + } + , issues: function(user, repo, cb) { + return new Repo(user, repo).fetchIssues(cb) + } + , languages: function(user, repo, cb) { + return new Repo(user, repo).fetchLanguages(cb) + } + , tags: function(user, repo, cb) { + return new Repo(user, repo).fetchTags(cb) + } + , watchers: function(user, repo, cb) { + return new Repo(user, repo).fetchWatchers(cb) + } + + , ref: function(user, repo, name, cb) { + return new Ref(user, repo, name, cb) + } + + // Tag + , tag: function(user, repo, name, cb) { + return new Tag(user, repo, name, cb) + } + + // Tree + , tree: function(user, repo, sha, cb) { + return new Tree(user, repo, sha, cb) + } + + // User + , user: function(login, cb) { + return new User(login, cb) + } + , followers: function(login, cb) { + return new User(login).fetchFollowers(cb) + } + , following: function(login, cb) { + return new User(login).fetchFollowing(cb) + } + , repos: function(login, cb) { + return new User(login).fetchRepos(cb) + } + , watched: function(login, cb) { + return new User(login).fetchWatched(cb) + } + + // Why not, expose the resources directly as well. + , Blob: Blob + , Branch: Branch + , Commit: Commit + , Download: Download + , Issue: Issue + , Org: Org + , Ref: Ref + , Repo: Repo + , Tree: Tree + , User: User + } + + // when running in the browser request is set later, in shim() + var request + + if (isBrowser) { + shim() + global.GITR = api + } + else { + var https = require('https') + request = function(options, cb) { + var req = https.request(options, function(response) { + var bodyParts = [] + response.on('data', function(b) { bodyParts.push(b) }) + response.on('end', function() { + var body = bodyParts.join('') + if (response.statusCode === 200) { + cb(null, body, response) + } + else { + console.dir(options, response, body) + var err = new Error('http error') + err.statusCode = response.statusCode + err.body = body + cb(err) + } + }) + }) + req.end() + req.on('error', function(err) { cb(err) }) + } + module.exports = api + } + + + // Generic Resource // + // + // Used as the prototype by createResource. Provides + // methods for fetching the resource and related + // sub-resources. + function Resource() {} + + // Fetch data for this resource and pass it to the + // callback after mixing the data into the object. + // Data is also available via the `data` property. + Resource.prototype.fetch = function(cb) { + if (this.data) { + cb(null, this.data) + } + else { + var self = this + fetch(this.path, function(err, data) { + // console.log('FETCH', self.path, err, data) + if (err) { + // console.log(err) + } + else { + self.data = data + mixin(self, data) + } + if (typeof cb === 'function') { + cb.call(self, err, data) + } + }) + } + return this + } + + Resource.prototype.fetchSubResource = function(thing, cb) { + if (this['_' + thing]) { + cb(null, this['_' + thing]) + } + else { + var self = this + fetch(this.path + '/' + thing, function(err, data) { + // console.log('FETCH SUBRESOURCE', self.path, thing, err, data) + if (err) { + // console.log(self.path, err) + } + else { + self['_' + thing] = data + } + if (typeof cb === 'function') { + cb.call(self, err, data) + } + }) + } + return this + } + + var __slice = Array.prototype.slice + + // Create a resource w/ Resource as the prototype. + // + // spec: an object with the following properties: + // + // - constructor: a constructor function + // - has: a list of resources belonging to this resource + // + // Typically the constructor accepts one or more arguments specifying + // the name or pieces of info identifying the specific resource and + // used to build the URL to fetch it. It also accepts an optional + // callback as the last parameter. + // + // The constructor must set the `path` property which is used to + // fetch the resource. + // + // If a callback is provided then the resource is immediately + // fetched and the callback is threaded through to the `fetch` + // method. The callback function has the signature + // `function(err, data)`. + // + // The `has` list specifies sub-resources, e.g. a user has repos, + // followers, etc. An organization has members. + // + // Each related sub-resource gets a method named appropriately, + // e.g. the User resource has followers so User objects have a + // `fetchFollowers` method. + function createResource(spec) { + var subResources = spec.has ? __slice.call(spec.has) : null + , resource = function(/* ..., cb */) { + var args = __slice.call(arguments) + , lastArgIsCallback = typeof args[args.length - 1] === 'function' + , cb = lastArgIsCallback ? args.pop() : null + , result = spec.constructor.apply(this, args) + + if (typeof cb === 'function') { + this.fetch(cb) + } + + return result + } + + inherits(resource, Resource) + + if (subResources) { + subResources.forEach(function(thing) { + var fnName = 'fetch' + toTitleCase(thing) + resource.prototype[fnName] = function(cb) { + return this.fetchSubResource(thing, cb) + } + }) + } + + return resource + } + + + // Define Resources // + + var Blob = createResource({ + constructor: function(user, repo, sha) { + this.user = user + this.repo = repo + this.sha = sha + this.path = '/repos/' + [user, repo].map(encodeURIComponent).join('/') + '/git/blobs/' + sha + } + }) + + var Branch = createResource({ + constructor: function (user, repo, name) { + this.user = user + this.repo = repo + this.name = name + this.path = '/repos/' + [user, repo].map(encodeURIComponent).join('/') + '/git/refs/heads/' + name + } + }) + + var Commit = createResource({ + constructor: function Commit(user, repo, sha) { + this.user = user + this.repo = repo + this.sha = sha + this.path = '/repos/' + [user, repo].map(encodeURIComponent).join('/') + '/git/commits/' + sha + } + }) + + var Download = createResource({ + constructor: function(user, repo, id) { + this.user = user + this.repo = repo + this.id = id + this.path = '/repos/' + [user, repo].map(encodeURIComponent).join('/') + '/downloads/' + id + } + }) + + var Issue = createResource({ + constructor: function(user, repo, id) { + this.user = user + this.repo = repo + this.id = id + this.path = '/repos/' + [user, repo].map(encodeURIComponent).join('/') + '/issues/' + id + } + }) + + var Org = createResource({ + constructor: function(name) { + this.name = name + this.path = '/orgs/' + encodeURIComponent(nam) + } + + , has: 'members repos'.split(' ') + }) + + var Ref = createResource({ + constructor: function (user, repo, name) { + this.user = user + this.repo = repo + this.name = name + this.path = '/repos/' + [user, repo].map(encodeURIComponent).join('/') + '/git/refs/' + name + } + }) + + var Repo = createResource({ + constructor: function(user, repo) { + this.user = user + this.repo = repo + this.path = '/repos/' + [user, repo].map(encodeURIComponent).join('/') + } + + , has: ('branches collaborators contributors downloads' + + ' forks languages tags teams watchers').split(' ') + }) + + var Tag = createResource({ + constructor: function (user, repo, name) { + this.user = user + this.repo = repo + this.name = name + this.path = '/repos/' + [user, repo].map(encodeURIComponent).join('/') + '/git/refs/tags/' + name + } + }) + + var Tree = createResource({ + constructor: function(user, repo, sha) { + this.user = user + this.repo = repo + this.sha = sha + this.path = '/repos/' + [user, repo].map(encodeURIComponent).join('/') + '/git/trees/' + sha + } + }) + + var User = createResource({ + constructor: function(login) { + // Allow creating a user from an object returned by the API + if (login.login) { + login = login.login + } + this.login = login + this.path = '/users/' + encodeURIComponent(login) + } + + , has: 'followers following repos watched'.split(' ') + }) + + + // Fetch data from github. JSON is parsed and keys are camelized. + // + // path: the path to the resource + // cb: callback(err, data) + function fetch(path, cb) { + request({ host: 'api.github.com', path: path }, function(err, body, response) { + // JSONP requests in the browser return the object directly + var data = body + + // Requests in Node return json text, try to parse it + if (response && /json/i.exec(response.headers['content-type'])) { + try { + data = JSON.parse(body) + } + catch (e) { + err = e + data = null + } + } + + cb(err, camelize(data)) + }) + } + + // created_at => createdAt + function camel(s) { + return s.replace(/_(.)/g, function(_, l) { return l.toUpperCase() }) + } + + // camelize all keys of an object, or all objects in an array + function camelize(obj) { + if (!obj || typeof obj === 'string') return obj + if (Array.isArray(obj)) return obj.map(camelize) + if (typeof obj === 'object') { + return Object.keys(obj).reduce(function(camelizedObj, k) { + camelizedObj[camel(k)] = camelize(obj[k]) + return camelizedObj + }, {}) + } + return obj + } + + function toTitleCase(s) { + return s.charAt(0).toUpperCase() + s.slice(1) + } + + function mixin(a, b) { + for (var k in b) { + if (b.hasOwnProperty(k)) a[k] = b[k] + } + } + + + // Browser Utilities // + + function shim() { + shimBind() + shimES5() + shimRequest() + } + + function shimBind() { + // bind from Prototype + if (!Function.prototype.bind) { + (function(){ + function update(array, args) { + var arrayLength = array.length, length = args.length + while (length--) array[arrayLength + length] = args[length] + return array + } + function merge(array, args) { + array = __slice.call(array, 0) + return update(array, args) + } + Function.prototype.bind = function(context) { + if (arguments.length < 2 && typeof arguments[0] === 'undefined') return this + var __method = this, args = __slice.call(arguments, 1) + return function() { + var a = merge(args, arguments) + return __method.apply(context, a) + } + } + }()) + } + } + + // a few functions from Kris Kowal's es5-shim + // https://github.com/kriskowal/es5-shim + function shimES5() { + var has = Object.prototype.hasOwnProperty + + // ES5 15.2.3.6 + if (!Object.defineProperty || ie === 8) { // ie8 + Object.defineProperty = function(object, property, descriptor) { + if (typeof descriptor == "object" && object.__defineGetter__) { + if (has.call(descriptor, "value")) { + if (!object.__lookupGetter__(property) && !object.__lookupSetter__(property)) { + // data property defined and no pre-existing accessors + object[property] = descriptor.value + } + if (has.call(descriptor, "get") || has.call(descriptor, "set")) { + // descriptor has a value property but accessor already exists + throw new TypeError("Object doesn't support this action") + } + } + // fail silently if "writable", "enumerable", or "configurable" + // are requested but not supported + else if (typeof descriptor.get == "function") { + object.__defineGetter__(property, descriptor.get) + } + if (typeof descriptor.set == "function") { + object.__defineSetter__(property, descriptor.set) + } + } + return object + } + } + + // ES5 15.2.3.14 + // http://whattheheadsaid.com/2010/10/a-safer-object-keys-compatibility-implementation + if (!Object.keys) { // ie8 + (function() { + var hasDontEnumBug = true, + dontEnums = [ + 'toString', + 'toLocaleString', + 'valueOf', + 'hasOwnProperty', + 'isPrototypeOf', + 'propertyIsEnumerable', + 'constructor' + ], + dontEnumsLength = dontEnums.length + + for (var key in {"toString": null}) { + hasDontEnumBug = false + } + + Object.keys = function (object) { + + if ( + typeof object !== "object" && typeof object !== "function" + || object === null + ) + throw new TypeError("Object.keys called on a non-object") + + var keys = [] + for (var name in object) { + if (has.call(object, name)) { + keys.push(name) + } + } + + if (hasDontEnumBug) { + for (var i = 0, ii = dontEnumsLength; i < ii; i++) { + var dontEnum = dontEnums[i] + if (has.call(object, dontEnum)) { + keys.push(dontEnum) + } + } + } + + return keys + } + }()) + } // Object.keys + + // + // Array + // ===== + // + + // ES5 15.4.3.2 + if (!Array.isArray) { + Array.isArray = function(obj) { + return Object.prototype.toString.call(obj) == "[object Array]" + } + } + + // ES5 15.4.4.18 + if (!Array.prototype.forEach) { // ie8 + Array.prototype.forEach = function(block, thisObject) { + var len = this.length >>> 0 + for (var i = 0; i < len; i++) { + if (i in this) { + block.call(thisObject, this[i], i, this) + } + } + } + } + + // ES5 15.4.4.19 + // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/map + if (!Array.prototype.map) { // ie8 + Array.prototype.map = function(fun /*, thisp*/) { + var len = this.length >>> 0 + if (typeof fun != "function") { + throw new TypeError() + } + + var res = new Array(len) + var thisp = arguments[1] + for (var i = 0; i < len; i++) { + if (i in this) { + res[i] = fun.call(thisp, this[i], i, this) + } + } + + return res + } + } + + // ES5 15.4.4.20 + if (!Array.prototype.filter) { // ie8 + Array.prototype.filter = function (block /*, thisp */) { + var values = [] + , thisp = arguments[1] + for (var i = 0; i < this.length; i++) { + if (block.call(thisp, this[i])) { + values.push(this[i]) + } + } + return values + } + } + + // ES5 15.4.4.21 + // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/reduce + if (!Array.prototype.reduce) { // ie8 + Array.prototype.reduce = function(fun /*, initial*/) { + var len = this.length >>> 0 + if (typeof fun != "function") { + throw new TypeError() + } + + // no value to return if no initial value and an empty array + if (len == 0 && arguments.length == 1) { + throw new TypeError() + } + + var i = 0 + if (arguments.length >= 2) { + var rv = arguments[1] + } else { + do { + if (i in this) { + rv = this[i++] + break + } + + // if array contains no values, no initial value to return + if (++i >= len) { + throw new TypeError() + } + } while (true) + } + + for (; i < len; i++) { + if (i in this) { + rv = fun.call(null, rv, this[i], i, this) + } + } + + return rv + } + } // Array.prototype.reduce + } // function shimES5() + + // jsonp request, quacks like node's http.request + function shimRequest() { + var load, _jsonpCounter = 1 + + // request is declared earlier + request = function(options, cb) { + var jsonpCallbackName = '_jsonpCallback' + _jsonpCounter++ + , url = 'https://' + options.host + options.path + '?callback=GITR.' + jsonpCallbackName + GITR[jsonpCallbackName] = function(response) { + if (response.meta.status >= 200 && response.meta.status < 300) { + cb(null, response.data) + } + else { + var err = new Error('http error') + err.statusCode = response.meta.status + err.response = response + cb(err) + } + setTimeout(function() { delete GITR[jsonpCallbackName] }, 0) + } + load(url) + } + + // bootstrap loader from LABjs (load is declared earlier) + load = function(url) { + var oDOC = document + , handler + , head = oDOC.head || oDOC.getElementsByTagName("head") + + // loading code borrowed directly from LABjs itself + // (now removes script elem when done and nullifies its reference --sjs) + setTimeout(function () { + if ("item" in head) { // check if ref is still a live node list + if (!head[0]) { // append_to node not yet ready + setTimeout(arguments.callee, 25) + return + } + head = head[0]; // reassign from live node list ref to pure node ref -- avoids nasty IE bug where changes to DOM invalidate live node lists + } + var scriptElem = oDOC.createElement("script") + , scriptdone = false + + scriptElem.onload = scriptElem.onreadystatechange = function () { + if ((scriptElem.readyState && scriptElem.readyState !== "complete" && scriptElem.readyState !== "loaded") || scriptdone) { + return false + } + scriptElem.onload = scriptElem.onreadystatechange = null + scriptElem.parentNode.removeChild(scriptElem) + scriptElem = null + scriptdone = true + } + scriptElem.src = url + head.insertBefore(scriptElem, head.firstChild) + }, 0) // setTimeout + + // required: shim for FF <= 3.5 not having document.readyState + if (oDOC.readyState == null && oDOC.addEventListener) { + oDOC.readyState = "loading" + oDOC.addEventListener("DOMContentLoaded", function handler() { + oDOC.removeEventListener("DOMContentLoaded", handler, false) + oDOC.readyState = "complete" + }, false) + } + + } // function load(url) + + } // function shimRequest() + +}()) diff --git a/public/js/projects.js b/public/js/projects.js new file mode 100644 index 0000000..59fab02 --- /dev/null +++ b/public/js/projects.js @@ -0,0 +1,137 @@ +;(function() { + if (typeof console === 'undefined') { + console = {log:function(){}} + } + + var global = this + global.SJS = { + proj: function(name) { + SJS.projName = name + var data = createObjectStore(name) + if (document.addEventListener) { + document.addEventListener('DOMContentLoaded', ready, false) + } else if (window.attachEvent) { + window.attachEvent('onload', ready) + } + function ready() { + function addClass(el, name) { + var c = el.className || name + if (!c.match(new RegExp('\b' + name + '\b', 'i'))) c += ' ' + name + } + function html(id, h) { + document.getElementById(id).innerHTML = h + } + + var body = document.getElementsByTagName('body')[0] + , text + if ('innerText' in body) { + text = function(id, text) { + document.getElementById(id).innerText = text + } + } else { + text = function(id, text) { + document.getElementById(id).textContent = text + } + } + + function highlight(id) { + document.getElementById(id).style.className = ' highlight' + } + function textHighlight(id, t) { + text(id, t) + document.getElementById(id).className = ' highlight' + } + function hide(id) { + document.getElementById(id).style.display = 'none' + } + + function langsByUsage(langs) { + return Object.keys(langs).sort(function(a, b) { + return langs[a] < langs[b] ? -1 : 1 + }) + } + + function listify(things) { + return '' + } + + function updateBranches(name, branches) { + function branchLink(b) { + return '' + b.name + '' + } + html('branches', listify(branches.map(branchLink))) + } + + function updateContributors(contributors) { + function userLink(u) { + return '' + (u.name || u.login) + '' + } + html('contributors', listify(contributors.map(userLink))) + } + + function updateLangs(langs) { + html('langs', listify(langsByUsage(langs))) + } + + function updateN(name, things) { + textHighlight('n' + name, things.length) + if (things.length === 1) hide(name.charAt(0) + 'plural') + } + + var t = data.get('t-' + name) + if (!t || +new Date() - t > 3600 * 1000) { + console.log('stale ' + String(t)) + data.set('t-' + name, +new Date()) + GITR.repo('samsonjs', name) + .fetchBranches(function(err, branches) { + if (err) { + text('branches', '(oops)') + } else { + data.set('branches', branches) + updateBranches(name, branches) + } + }) + .fetchLanguages(function(err, langs) { + if (err) { + text('langs', '(oops)') + return + } + data.set('langs', langs) + updateLangs(langs) + }) + .fetchContributors(function(err, users) { + if (err) { + text('contributors', '(oops)') + } else { + data.set('contributors', users) + updateContributors(users) + } + }) + .fetchWatchers(function(err, users) { + if (err) { + text('nwatchers', '?') + } else { + data.set('watchers', users) + updateN('watchers', users) + } + }) + .fetchForks(function(err, repos) { + if (err) { + text('nforks', '?') + } else { + data.set('forks', repos) + updateN('forks', repos) + } + }) + } else { + console.log('hit ' + t + ' (' + (+new Date() - t) + ')') + updateBranches(name, data.get('branches')) + updateLangs(data.get('langs')) + updateContributors(data.get('contributors')) + updateN('watchers', data.get('watchers')) + updateN('forks', data.get('forks')) + } + } + } + } +}()); diff --git a/public/js/storage-polyfill.js b/public/js/storage-polyfill.js new file mode 100644 index 0000000..8b35699 --- /dev/null +++ b/public/js/storage-polyfill.js @@ -0,0 +1,92 @@ +if (!window.localStorage || !window.sessionStorage) (function () { + +var Storage = function (type) { + function createCookie(name, value, days) { + var date, expires; + + if (days) { + date = new Date(); + date.setTime(date.getTime()+(days*24*60*60*1000)); + expires = "; expires="+date.toGMTString(); + } else { + expires = ""; + } + document.cookie = name+"="+value+expires+"; path=/"; + } + + function readCookie(name) { + var nameEQ = name + "=", + ca = document.cookie.split(';'), + i, c; + + for (i=0; i < ca.length; i++) { + c = ca[i]; + while (c.charAt(0)==' ') { + c = c.substring(1,c.length); + } + + if (c.indexOf(nameEQ) == 0) { + return c.substring(nameEQ.length,c.length); + } + } + return null; + } + + function setData(data) { + data = JSON.stringify(data); + if (type == 'session') { + window.top.name = data; + } else { + createCookie('localStorage', data, 365); + } + } + + function clearData() { + if (type == 'session') { + window.top.name = ''; + } else { + createCookie('localStorage', '', 365); + } + } + + function getData() { + var data = type == 'session' ? window.top.name : readCookie('localStorage'); + return data ? JSON.parse(data) : {}; + } + + + // initialise if there's already data + var data = getData(); + + return { + clear: function () { + data = {}; + clearData(); + }, + getItem: function (key) { + return data[key] || null; + }, + key: function (i) { + // not perfect, but works + var ctr = 0; + for (var k in data) { + if (ctr == i) return k; + else ctr++; + } + return null; + }, + removeItem: function (key) { + delete data[key]; + setData(data); + }, + setItem: function (key, value) { + data[key] = value+''; // forces the value to a string + setData(data); + } + }; +}; + +if (!window.localStorage) window.localStorage = new Storage('local'); +if (!window.sessionStorage) window.sessionStorage = new Storage('session'); + +}()); diff --git a/public/js/store.js b/public/js/store.js new file mode 100644 index 0000000..291eafc --- /dev/null +++ b/public/js/store.js @@ -0,0 +1,51 @@ +;(function() { + var global = this + if (typeof localStorage !== 'undefined') { + global.createObjectStore = function(namespace) { + function makeKey(k) { + return '--' + namespace + '-' + (k || '') + } + return { + clear: function() { + var i = localStorage.length + , k + , prefix = new RegExp('^' + makeKey()) + while (--i) { + k = localStorage.key(i) + if (k.match(prefix)) { + localStorage.remove(k) + } + } + }, + get: function(key) { + var val = localStorage[makeKey(key)] + try { + while (typeof val === 'string') val = JSON.parse(val) + } catch (e) { + //console.log('string?') + } + return val + }, + set: function(key, val) { + localStorage[makeKey(key)] = typeof val === 'string' ? val : JSON.stringify(val) + }, + remove: function(key) { + delete localStorage[makeKey(key)] + } + } + } + global.ObjectStore = createObjectStore('default') + } else { + // Create an in-memory store, should probably fall back to cookies + global.createObjectStore = function() { + var store = {} + return { + clear: function() { store = {} }, + get: function(key) { return store[key] }, + set: function(key, val) { store[key] = val }, + remove: function(key) { delete store[key] } + } + } + global.ObjectStore = createObjectStore() + } +}()); \ No newline at end of file