Commits

Anonymous committed 035571a Merge

merge

Comments (0)

Files changed (79)

 Kirill A. Korinskiy
 Kevin Smith
 Jonathan Lee
+Sean Cribbs

client_lib/jiak.rb

 #     Version 2.0 (the "License"); you may not use this file
 #     except in compliance with the License.  You may obtain
 #     a copy of the License at
-   
+
 #       http://www.apache.org/licenses/LICENSE-2.0
-   
+
 #     Unless required by applicable law or agreed to in writing,
 #     software distributed under the License is distributed on an
 #     "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 #     KIND, either express or implied.  See the License for the
 #     specific language governing permissions and limitations
-#     under the License.    
+#     under the License.
 
 # A Ruby interface for speaking to Riak.
 # Example usage code can be found at the end of this file.
 # This library requires that you have a library providing 'json'
 # installed ("gem install json" will get you one).
+#
+# 
+
 require 'net/http'
 require 'rubygems'
 require 'json'
 
-class JiakClient
-  def initialize(ip, port, jiakPrefix='/jiak/', options={})
-    @ip = ip
-    @port = port
-    @prefix = jiakPrefix
-    @opts = options
+module Riak
 
-    if (@opts['clientId'])
-      if (@opts['clientId'].kind_of? Integer &&
-          @opts['clientId'] > 0 &&
-          @opts['clientId'] < 4294967296)
-        @opts['clientId'] = base64(@opts['clientId'])
+  class ClientException < ::Exception; end
+
+  class Client
+    MAX_CLIENT_ID = 4294967296
+    
+    def initialize(ip, port, jiakPrefix='/jiak/', options={})
+      @ip, @port, @prefix, @opts = ip, port, jiakPrefix, options
+
+      clientId = @opts['clientId']
+      if clientId
+        @opts['clientId'] = base64(clientId) if (0..MAX_CLIENT_ID).include?(clientId)
+      else
+        @opts['clientId'] = base64(rand(MAX_CLIENT_ID))
       end
-    else
-      @opts['clientId'] = base64(rand(4294967296))
-    end
-  end
-
-  # Set the schema for 'bucket'.  The schema parameter
-  # must be a hash with at least an 'allowed_fields' field.
-  # Other valid fields are 'requried_fields', 'read_mask',
-  # and 'write_mask'
-  def set_bucket_schema(bucket, schema)
-    if (!schema['required_fields'])
-      schema['required_fields'] = []
-    end
-    if (!schema['read_mask'])
-      schema['read_mask'] = schema['allowed_fields']
-    end
-    if (!schema['write_mask'])
-      schema['write_mask'] = schema['read_mask']
     end
 
-    do_req(set_data(Net::HTTP::Put.new(path(bucket)),
-                    {'schema'=>schema}),
-           '204')
-  end
+    # Set the schema for 'bucket'.  The schema parameter
+    # must be a hash with at least an 'allowed_fields' field.
+    # Other valid fields are 'requried_fields', 'read_mask',
+    # and 'write_mask'
+    def set_bucket_schema(bucket, schema)
+      schema['required_fields'] ||= []
+      schema['read_mask']       ||= schema['required_fields']
+      schema['write_mask']      ||= schema['read_mask']
 
-  # Get the schema and key list for 'bucket'
-  def list_bucket(bucket)
-    do_req(Net::HTTP::Get.new(path(bucket)), '200')
-  end
-  
-  # Get the object stored in 'bucket' at 'key'
-  def fetch(bucket, key, r=nil)
-    do_req(Net::HTTP::Get.new(path(bucket, key,
-                                   {'r'=>(r||@opts['r'])})),
-           '200')
-  end
-
-  # Store 'object' in Riak.  If the object has not defined
-  # its 'key' field, a key will be chosen for it by the server.
-  def store(object, w=nil, dw=nil, r=nil)
-    q = {
-      'returnbody'=>'true',
-      'w'=>(w||@opts['w']),
-      'dw'=>(dw||@opts['dw']),
-      'r'=>(r||@opts['r'])
-    }
-    if (object['key'])
-      req = Net::HTTP::Put.new(path(object['bucket'], object['key'], q),
-                               initheader={"X-Riak-ClientId" => @opts['clientId']})
-      code = '200'
-    else
-      req = Net::HTTP::Post.new(path(object['bucket'], nil, q),
-                                initheader={"X-Riak-ClientId" => @opts['clientId']})
-      code = '201'
+      do_req(set_data(Net::HTTP::Put.new(path(bucket)),
+                      {'schema'=>schema}),
+             '204')
     end
 
-    do_req(set_data(req, object), code)
-  end
-
-  # Delete the data stored in 'bucket' at 'key'
-  def delete(bucket, key, rw=nil)
-    do_req(Net::HTTP::Delete.new(path(bucket, key,
-                                      {'rw'=>(rw||@opts['rw'])}),
-                                 initheader={"X-Riak-ClientId" => @opts['clientId']}),
-           '204')
-  end
-
-  # Follow links from the object stored in 'bucket' at 'key'
-  # to other objects.  The 'spec' parameter should be an array
-  # of hashes, each hash optinally defining 'bucket', 'tag',
-  # and 'acc' fields.  If a field is not defined in a spec hash,
-  # the wildcard '_' will be used instead.
-  def walk(bucket, key, spec)
-    do_req(Net::HTTP::Get.new(path(bucket, key)+convert_walk_spec(spec)),
-           '200')
-  end
-
-  def convert_walk_spec(spec)
-    acc = ''
-    spec.each do |step|
-      acc += URI.encode(step['bucket']||'_')+','+
-        URI.encode(step['tag']||'_')+','+
-        (step['acc']||'_')+'/'
-    end
-    acc
-  end
-
-  def path(bucket, key=nil, reqOpts={})
-    p = @prefix + URI.encode(bucket) + '/'
-    if (key)
-      p += URI.encode(key) + '/'
+    # Get the schema and key list for 'bucket'
+    def list_bucket(bucket)
+      do_req(Net::HTTP::Get.new(path(bucket)), '200')
     end
 
-    q = [];
-    reqOpts.each do |n,v|
-      if (v): q.push "#{n}=#{v}" end
+    # Get the object stored in 'bucket' at 'key'
+    def fetch(bucket, key, r=nil)
+      do_req(Net::HTTP::Get.new(path(bucket, key,
+                                     {'r'=>(r||@opts['r'])})),
+             '200')
     end
-    if (q.length > 0): p += '?'+(q.join('&')) end
 
-    p
+    # Store 'object' in Riak.  If the object has not defined
+    # its 'key' field, a key will be chosen for it by the server.
+    def store(object, w=nil, dw=nil, r=nil)
+      q = {
+        'returnbody'=>'true',
+        'w'=>(w||@opts['w']),
+        'dw'=>(dw||@opts['dw']),
+        'r'=>(r||@opts['r'])
+      }
+      if (object['key'])
+        req = Net::HTTP::Put.new(path(object['bucket'], object['key'], q),
+                                 initheader={"X-Riak-ClientId" => @opts['clientId']})
+        code = '200'
+      else
+        req = Net::HTTP::Post.new(path(object['bucket'], nil, q),
+                                  initheader={"X-Riak-ClientId" => @opts['clientId']})
+        code = '201'
+      end
+
+      do_req(set_data(req, object), code)
+    end
+
+    # Delete the data stored in 'bucket' at 'key'
+    def delete(bucket, key, rw=nil)
+      do_req(Net::HTTP::Delete.new(path(bucket, key,
+                                        {'rw'=>(rw||@opts['rw'])}),
+                                   initheader={"X-Riak-ClientId" => @opts['clientId']}),
+             '204')
+    end
+
+    # Follow links from the object stored in 'bucket' at 'key'
+    # to other objects.  The 'spec' parameter should be an array
+    # of hashes, each hash optinally defining 'bucket', 'tag',
+    # and 'acc' fields.  If a field is not defined in a spec hash,
+    # the wildcard '_' will be used instead.
+    def walk(bucket, key, spec)
+      do_req(Net::HTTP::Get.new(path(bucket, key)+convert_walk_spec(spec)),
+             '200')
+    end
+
+    private
+      def convert_walk_spec(spec)
+        spec.map do |step|
+          URI.encode [(step['bucket']||'_'),(step['tag']||'_'),(step['acc']||'_')].join(",")
+        end.join("/")
+      end
+
+      def path(bucket, key=nil, reqOpts={})
+        path = URI.encode(@prefix + [bucket, key].compact.join("/"))
+        q = URI.encode(reqOpts.map {|k,v| !v.nil? && "#{k}=#{v}" }.compact.join("&"))
+        q.empty? ? path : [path, q].join("?")
+      end
+
+      def set_data(req, data)
+        req.content_type='application/json'
+        req.body=JSON.generate(data)
+        req
+      end
+
+      def do_req(req, expect)
+        res = Net::HTTP.start(@ip, @port) {|http|
+          http.request(req)
+        }
+        if (res.code == expect)
+          res.body ? JSON.parse(res.body) : true
+        else
+          raise ClientException.new(res.code+' '+res.message+' '+res.body)
+        end
+      end
+
+      def base64(n)
+        base64digits = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
+        "%c%c%c%c%c%c==" %
+          [base64digits[(n >> 26)],
+           base64digits[((n >> 20)&63)],
+           base64digits[((n >> 14)&63)],
+           base64digits[((n >> 8)&63)],
+           base64digits[((n >> 2)&63)],
+           base64digits[((n << 4)&63)]]
+      end
   end
-
-  def set_data(req, data)
-    req.content_type='application/json'
-    req.body=JSON.generate(data)
-    req
-  end
-
-  def do_req(req, expect)
-    res = Net::HTTP.start(@ip, @port) {|http|
-      http.request(req)
-    }
-    if (res.code == expect)
-      res.body ? JSON.parse(res.body) : true
-    else
-      raise JiakException.new(res.code+' '+res.message+' '+res.body)
-    end
-  end
-
-  def base64(n)
-    base64digits = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
-    "%c%c%c%c%c%c==" %
-      [base64digits[(n >> 26)],
-       base64digits[((n >> 20)&63)],
-       base64digits[((n >> 14)&63)],
-       base64digits[((n >> 8)&63)],
-       base64digits[((n >> 2)&63)],
-       base64digits[((n << 4)&63)]]
-  end
-  private:convert_walk_spec
-  private:path
-  private:set_data
-  private:do_req
-  private:base64
 end
 
-class JiakException<Exception
- end
-
 # Example usage
 if __FILE__ == $0
-  jc = JiakClient.new("127.0.0.1", 8000)
+  jc = Riak::Client.new("127.0.0.1", 8098)
 
   puts "Creating bucket foo..."
   jc.set_bucket_schema('foo', {'allowed_fields'=>['bar','baz']})

demo/stickynotes/Emakefile

-% -*- mode: erlang -*-
-{["src/*"], 
- [{i, "include"},
-  {outdir, "ebin"},
-  debug_info]
-}.

demo/stickynotes/Makefile

-ERL          ?= erl
-EBIN_DIRS    := $(wildcard deps/*/ebin) $(wildcard deps/*/deps/*/ebin)
-APP          := stickynotes
-
-all: erl ebin/$(APP).app
-
-erl:
-	@$(ERL) -pa $(EBIN_DIRS) -noinput +B \
-	  -eval 'case make:all() of up_to_date -> halt(0); error -> halt(1) end.'
-
-docs:
-	@erl -noshell -run edoc_run application '$(APP)' '"."' '[]'
-
-clean: 
-	@echo "removing:"
-	@rm -fv ebin/*.beam ebin/*.app
-
-ebin/$(APP).app: src/$(APP).app
-	@cp -v src/$(APP).app $@

demo/stickynotes/README

-Stickynotes is a demo Riak application.
-
-Running
----
-
-cd $RIAK_HOME  # base of your riak installation
-./start-fresh demo/stickynotes/riak-config.erlenv
-cd demo/stickynotes
-make
-./start.sh
-
-Now point your browser at http://localhost:8000/
-
-You should see a dark grey page with a few controls
-at the top.  Start clicking!
-
-
-Structure
----
-
-Stickynotes starts a Webmachine node, which exposes a simple
-file-serving resource, as well as jiak_resource and jaywalker_resource
-from Riak.  The files you'll find here:
-
-/
- Makefile            - for rebuilding things
- README              - this file
- riak-config.erlenv  - Riak cluster configuration file for Stickynotes
- start.sh            - start the Stickynotes application
- start-dev.sh        - start Stickynotes with auto-module-reloading
- deps/
-   riak              - symlink to riak for code path loading
- priv/www/
-   index.html        - the HTML you see at /
-   css/
-     application.css - the CSS you see at /
-   images/           - all of the images
-   js/
-     application.js  - the logic running the page
-     jiak.js         - libary for querying Riak through Jiak
-     jquery*.js      - jQuery
-     json2.js        - JSON library
- src/
-   groups.erl        - the Jiak bucket module for groups objects
-   notes.erl         - the Jiak bucket module for notes objects
-   stickynotes.app   - application configuration (this is where
-                       riak_ip, _port, _cookie are set)
-   stickynotes.erl   - top application file
-   stickynotes_app.erl - application start file
-   stickynotes_deps.erl - utilities for getting beams from other
-                            applications loaded in Stickynotes
-   stickynotes_resource.erl - simple file-serving resource
-   stickynotes_sup.erl - supervisor that starts the Webmachine node

demo/stickynotes/deps/webmachine

-../../../deps/webmachine
Add a comment to this file

demo/stickynotes/ebin/.hg_dummy

Empty file removed.

demo/stickynotes/priv/www/css/application.css

-html {
-	background-color: #323232;
-}
-
-body{
-	padding:0;
-	margin:0;
-	width: 100%;
-	height: 100%;
-	position: relative;
-}
-
-div#cache, div#templates {
-	display: none;
-}
-
-div#main{
-	width: 900px;
-	margin: 0 auto;
-	position: relative;
-}
-
-div#groups {
-    width: 750px;
-    margin: 0 75px;
-    position: relative;
-}
-
-div#board {
-	position: relative;
-	clear:both;
-	width: 100%;
-	height: 100%;
-}
-
-div#icons {
-	position: absolute;
-	top: 0;
-	width: 100%;
-	height: 50px;
-}
-
-div.add {
-	position:relative;
-	z-index: 999999999;
-	cursor: pointer;
-	width: 50px;
-	height: 50px;
-	background: transparent url('/images/add.png') 0 0 no-repeat;
-}
-
-div.add:hover {
-	background: transparent url('/images/add_hover.png') 0 0 no-repeat;
-}
-
-div.trash {
-	position: relative;
-	float: right;
-	width: 50px;
-	height: 50px;
-	background: transparent url('/images/trash.png') 0 0 no-repeat;
-}
-
-div.trash-active {
-	background: transparent url('/images/trash_active.png') 0 0 no-repeat;
-}
-
-div.trash-hover {
-	background: transparent url('/images/trash_hover.png') 0 0 no-repeat;
-}
-
-div.group {
-    width: 80px;
-    height: 25px;
-    padding: 5px 10px;
-    margin: 0 1px;
-    margin: 0;
-    background-color: #666;
-    float:left;
-}
-
-div.group.active {
-    background-color: #999;
-}
-
-div.group input {
-    cursor: text;
-    overflow: hidden;
-    font-size: 18px;
-    font-weight: normal;
-    font-family: "Comic Sans MS", "DejaVu Sans Mono";
-    width: 80px;
-    height: 40px;
-    margin: 0 auto;
-    border: none;
-    background-color: transparent;
-    color: #fff;
-}
-
-div#groups div.new {
-    float:left;
-    height: 25px;
-    background-color: #444;
-    font-size: 24px;
-    padding: 5px 10px;
-    color: #fff;
-}
-
-div#groups div.new:hover {
-    background-color: #666;
-    cursor: pointer;
-}
-
-div.note {
-	position:absolute;
-	top: 50px;
-	cursor: move;
-	text-align: center;
-	width: 275px;
-	height: 270px;
-	padding: 20px 0 0 0 ;
-}
-
-div.note textarea {
-	cursor: text;
-	overflow: hidden;
-	font-size: 18px;
-	font-weight: normal;
-	font-family: "Comic Sans MS", "DejaVu Sans Mono";
-	width: 230px;
-	height: 210px;
-	margin: 0 auto;
-	border: none;
-	background-color: transparent;
-}
-
-div.note.pink {
-	background: transparent url('/images/postit_pink.png') 0 0 no-repeat;
-}
-
-div.note.yellow {
-	background: transparent url('/images/postit_yellow.png') 0 0 no-repeat;
-}
-
-div.note.green {
-	background: transparent url('/images/postit_green.png') 0 0 no-repeat;
-}
-
-div.note.blue {
-	background: transparent url('/images/postit_blue.png') 0 0 no-repeat;
-}
-
-div.note.pink textarea {
-	color: #40176b;
-}
-
-div.note.yellow textarea {
-	color: blue;
-}
-
-div.note.green textarea{
-	color: #364457;
-}
-
-div.note.blue textarea{
-	color: #424242;
-}
-
-div.picker {
-	position:absolute;
-	cursor: pointer;
-	top: 0;
-	left: 240px;
-	width: 10px;
-	height: 10px;
-	padding: 5px;
-	background: transparent url('/images/color_picker.png') 50% 50% no-repeat;
-}
-
-div.picker:hover {
-	background: transparent url('/images/color_picker_hover.png') 50% 50% no-repeat;
-}
Add a comment to this file

demo/stickynotes/priv/www/images/add.png

Removed
Old image
Add a comment to this file

demo/stickynotes/priv/www/images/add_hover.png

Removed
Old image
Add a comment to this file

demo/stickynotes/priv/www/images/color_picker.png

Removed
Old image
Add a comment to this file

demo/stickynotes/priv/www/images/color_picker_hover.png

Removed
Old image
Add a comment to this file

demo/stickynotes/priv/www/images/postit_blue.png

Removed
Old image
Add a comment to this file

demo/stickynotes/priv/www/images/postit_green.png

Removed
Old image
Add a comment to this file

demo/stickynotes/priv/www/images/postit_pink.png

Removed
Old image
Add a comment to this file

demo/stickynotes/priv/www/images/postit_yellow.png

Removed
Old image
Add a comment to this file

demo/stickynotes/priv/www/images/trash.png

Removed
Old image
Add a comment to this file

demo/stickynotes/priv/www/images/trash_active.png

Removed
Old image
Add a comment to this file

demo/stickynotes/priv/www/images/trash_hover.png

Removed
Old image

demo/stickynotes/priv/www/index.html

-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-us" lang="en-us">
-	<head>
-		<title>stickyNotes</title>
-
-		<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
-
-		<link href="css/application.css" rel="stylesheet" type="text/css"/>
-
-		<script src="js/json2.js" type="text/javascript"></script>
-		<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js" type="text/javascript"></script>
-		<script src="js/jquery-ui-personalized-1.6rc2.min.js" type="text/javascript"></script>
-                <script src="js/jiak.js" type="text/javascript"></script>
-		<script src="js/application.js" type="text/javascript"></script>
-
-	</head>
-	<body>
-
-		<div id="cache">
-			<img src="/images/add_hover.png"/>
-			<img src="/images/color_picker_hover.png"/>
-			<img src="/images/postit_pink.png"/>
-			<img src="/images/postit_yellow.png"/>
-			<img src="/images/postit_green.png"/>
-			<img src="/images/postit_blue.png"/>
-			<img src="/images/trash_active.png"/>
-			<img src="/images/trash_hover.png"/>
-		</div>
-
-		<div id="templates">
-			<div class="note">
-				<div class="picker"></div>
-				<textarea autocomplete="off">Lorem ipsum</textarea>
-			</div>
-                        <div class="group">
-                          <input type="text" value="Group A"></input>
-                        </div>
-		</div>
-
-		<div id="main">
-
-			<div id="icons">
-				<div class="trash"></div>
-				<div class="add"></div>
-			</div>
-
-                        <div id="groups">
-                          <!-- here comes the groups -->
-                          <div class="new">+</div>
-                        </div>
-
-			<div id="board">
-
-				<!-- here comes the notes -->
-
-			</div>
-
-		</div>
-
-	</body>
-</html>

demo/stickynotes/priv/www/js/application.js

-var zIndex = 0;
-var gIndex = 0;
-var colors = ["pink", "yellow", "green", "blue"];
-var CurrentGroupId = '';
-
-function renderNote(note) {
-    var noteDisplay = NoteTemplate.clone();
-
-    noteDisplay
-        .data('note', note)
-        .css('left', note.object.x)
-        .css('top', note.object.y)
-        .css('z-index', note.object.z)
-        .addClass(note.object.color)
-        .mousedown(zTop)
-        .draggable({opacity: 0.75, stop:dragfun})
-        .find('div.picker').click(pickColor).end()
-        .find('textarea').val(note.object.text).focus(zTop).blur(upd).end();
-
-    $("div#board").append(noteDisplay);
-    $('textarea', noteDisplay).focus();
-}
-
-function renderNotes(list) {
-    for (n in list) renderNote(list[n]);
-}
-
-function renderGroup(group) {
-    var groupDisplay = GroupTemplate.clone();
-
-    groupDisplay
-        .data('group', group)
-        .attr('id', 'group_'+group.key)
-        .click(function() { loadGroup(group.key); })
-        .find('input').val(group.object.name).blur(updGrp).end();
-
-    if (CurrentGroupId == group.key)
-        groupDisplay.addClass('active');
-
-    $("div#groups div.new").before(groupDisplay);
-}
-
-function loadGroup(groupid) {
-    CurrentGroupId = groupid;
-    $("div#board").empty();
-    Client.walk(['groups', groupid],
-                [{bucket:'notes'}],
-                function(res) {
-                    renderNotes(res.results[0]);
-                },
-                true);
-    $('div.group').removeClass('active').removeClass('active');
-    $('div#group_'+groupid).addClass('active');
-}
-
-function topOfNoteDisplay(noteDisplay) {
-    return noteDisplay.is('div.note') ? noteDisplay : noteDisplay.parents('div.note');
-}
-
-function dragfun(el, ui) {
-    var noteDisplay = topOfNoteDisplay($(el.target));
-    var note = noteDisplay.data('note');
-    if (!note) return; //delete happened
-
-    note.object.x = ui.position.left;
-    note.object.y = ui.position.top;
-    upd(el);
-};
-
-function zTop(el) {
-    var noteDisplay = topOfNoteDisplay($(el.target));
-    zIndex += 1; 
-    noteDisplay.css('z-index', zIndex); 
-    noteDisplay.data('note').z = zIndex;
-}
-
-function pickColor(el) {
-    var noteDisplay = topOfNoteDisplay($(el.target));
-    var note = noteDisplay.data('note');
-
-    var color = note.object.color;
-    var nextIndex = colors.indexOf(color) + 1;
-    if (nextIndex >= colors.length) nextIndex = 0;
-    var nextColor = colors[nextIndex];
-
-    note.object.color = nextColor;
-    noteDisplay.removeClass(color).addClass(nextColor);
-    upd(el);
-}
-
-function upd(el) {
-    var noteDisplay = topOfNoteDisplay($(el.target));
-    var note = noteDisplay.data('note');
-    note.object.text = $('textarea', noteDisplay).val();
-
-    Client.store(note, function(updated) {
-        noteDisplay.data('note', updated);
-    });
-}
-
-function topOfGroupDisplay(groupDisplay) {
-    return groupDisplay.is('div.group') ? groupDisplay : groupDisplay.parents('div.group');
-}
-
-function updGrp(el) {
-    var groupDisplay = topOfGroupDisplay($(el.target));
-    var group = groupDisplay.data('group');
-    group.object.name = $('input', groupDisplay).val();
-    
-    Client.store(group, function(updated) {
-        groupDisplay.data('group', updated);
-    });
-}
-
-function rand(upper){
-    return Math.floor(Math.random()*upper) + 50
-}
-
-function init(){
-    NoteTemplate = $("div#templates div.note");
-    GroupTemplate = $("div#templates div.group");
-    Client = new JiakClient('/jiak/');
-
-    $("div.trash").droppable({
- 	accept: "div.note",
- 	activeClass: 'trash-active',
- 	hoverClass: "trash-hover",
-	tolerance: "touch",
- 	drop: function(e, ui) {
-            var noteid = $(ui.draggable).data('note').key;
-	    $(ui.draggable).draggable("destroy").fadeOut().remove();
-	    Client.remove("notes", noteid, null);
-	}	
-    });
-    
-    Client.fetch("groups", "", function(bucket) {
-        if (bucket.keys.length > 0) {
-            for (var i in bucket.keys)
-                Client.fetch("groups", bucket.keys[i], renderGroup);
-
-            loadGroup(bucket.keys[i]);
-        } else {
-            var firstGroup = {
-                "bucket":"groups",
-                "object":{"name":"Group A"},
-                "links":[]
-            };
-            Client.store(firstGroup, function(newgroup) {
-                renderGroup(newgroup);
-                loadGroup(newgroup.key)
-            });
-        }
-    });
-}
-
-$(document).ready(function(){
-    $('div.add').click(function(){
-	zIndex += 1;
-	var note = {
-	    "bucket": "notes",
-	    "object": {
-		"text": "",
-		"x": rand(50),
-		"y": rand(50),
-		"z": zIndex,
-		"color": "yellow"
-	    },
-            "links":[["groups", CurrentGroupId, "mygroup"]]
-	}
-	Client.store(note, renderNote);
-    });
-    $('div.new').click(function() {
-        gIndex += 1;
-        var group = {
-            "bucket": "groups",
-            "object": {
-                "name":"Group "+gIndex
-            },
-            "links":[]
-        }
-        Client.store(group, renderGroup);
-    });
-    init();
-});

demo/stickynotes/priv/www/js/jiak.js

-// JiakClient assumes the presence of jQuery and JSON libraries
-// (or at least compatible $.ajax and JSON.stringify functions)
-//
-// Usage: instantiate a JiakClient with proper options, then use
-// the store, fetch, remove, and walk function to get access to
-// objects from Jiak.
-//
-// Examples:
-//   var Client = new JiakClient('/jiak/');
-//   Client.fetch('note', '123', function(note) {
-//      note.object.text = 'Hello World';
-//      Client.store(note);
-//   });
-//
-//   Client.store({'bucket':'note',
-//                 'object':{'text':'a new note'},
-//                 'links':[]},
-//                function(note) {
-//                  alert("new note's key: "+note.key);
-//                });
-//
-//   Client.walk(['note', '456'],
-//               [{'bucket':'person', 'tag':'author'}],
-//               function(data) {
-//                 var authors = data.results[0];
-//                 alert("note's author is: "+
-//                       authors[0].object.name);
-//               });
-//
-// Default R, W, DW, and RW values are all 2.  To use other
-// values, pass an options object of the form:
-//    {r: 1, // value you want for R
-//     w: 3, // value you want for W
-//     dw:1, // value you want for DW
-//     rw:1} // value you want for RW
-// or, pass the values as parameters to the store, fetch
-// and delete functions.
-
-// This file is provided to you under the Apache License,
-// Version 2.0 (the "License"); you may not use this file
-// except in compliance with the License.  You may obtain
-// a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied.  See the License for the
-// specific language governing permissions and limitations
-// under the License.    
-
-
-function JiakClient(BaseUrl, Opts) {
-    this.baseurl = BaseUrl;
-    if (!(this.baseurl.slice(-1) == '/'))
-        this.baseurl += '/';
-
-    this.opts = Opts||{};
-
-    // utility to convert an integer to base64-encoded 32-bits
-    base64 = function(N) {
-        var base64digits = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
-        return base64digits[(N >>> 26)]
-            +base64digits[((N >>> 20)&63)]
-            +base64digits[((N >>> 14)&63)]
-            +base64digits[((N >>> 8)&63)]
-            +base64digits[((N >>> 2)&63)]
-            +base64digits[((N << 4)&63)]
-            +'==';
-    }
-
-    if (('clientId' in this.opts) && !!this.opts.clientId) {
-        if (typeof this.opts.clientId == "number"
-            && this.opts.clientId > 0 && this.opts.clientId < 4294967296) {
-            this.opts.clientId = base64(this.opts.clientId);
-        }
-        //otherwise, just use whatever clientId was given
-    } else {
-        //choose a client id if the caller didn't provide one
-        this.opts.clientId = base64(Math.floor(Math.random()*4294967296));
-    }
-}
-
-JiakClient.prototype.store = function(Object, Callback, NoReturnBody, W, DW, R) {
-    var cid = this.opts.clientId;
-    var req = {
-        contentType: "application/json",
-        dataType: "json",
-        beforeSend: function(xhr) {
-            xhr.setRequestHeader("X-Riak-ClientId", cid);
-        }
-    };
-
-    if (this.opts.alwaysPost || !Object.key)
-        req.type = 'POST';
-    else
-        req.type = 'PUT';
-    
-    req.url = this.path(Object.bucket);
-    if (Object.key) req.url += Object.key;
-    
-    var q = false;
-    if (!(this.opts.noReturnBody || NoReturnBody)) {
-        req.url += '?returnbody=true';
-        q = true;
-    }
-
-    if (W || this.opts.w) {
-        req.url += (q?'&':'?')+'w='+(W||this.opts.w);
-        q = true;
-    }
-
-    if (DW || this.opts.dw) {
-        req.url += (q?'&':'?')+'dw='+(DW||this.opts.dw);
-        q = true;
-    }
-
-    if (R || this.opts.r)
-        req.url += (q?'&':'?')+'r='+(R||this.opts.r);
-
-    if (typeof Callback == 'function')
-        req.success = Callback;
-
-    req.data = JSON.stringify(Object);
-
-    return $.ajax(req);
-}
-
-JiakClient.prototype.fetch = function(Bucket, Key, Callback, R) {
-    return $.ajax({
-        url:      this.path(Bucket, Key)+
-                    ((R||this.opts.r)?('?r='+(R||this.opts.r)):''),
-        dataType: "json",
-        success:  Callback
-    });
-}
-
-JiakClient.prototype.remove = function(Bucket, Key, Callback, RW) {
-    var cid = this.opts.clientId;
-    return $.ajax({
-        type:    'DELETE',
-        url:     this.path(Bucket, Key)+
-                   ((RW||this.opts.rw)?('?rw='+(RW||this.opts.rw)):''),
-        success: Callback,
-        beforeSend: function(xhr) {
-            xhr.setRequestHeader("X-Riak-ClientId", cid);
-        }
-    });
-}
-
-JiakClient.prototype.walk = function(Start, Spec, Callback, Nocache) {
-    var req = {
-        dataType: "json",
-        success: Callback
-    };
-
-    // Start can be either and object with {bucket:B, key:K}
-    // or a list with [Bucket, Key, ...]
-    if ('bucket' in Start)
-        req.url = this.path(Start.bucket, Start.key)+'/';
-    else
-        req.url = this.path(Start[0], Start[1])+'/';
-
-    // Spec should be a list of objects with
-    //    {bucket:B, tag:T, acc:A}
-    // where B and T specify the bucket and tag to match in the link
-    //   or are undefined to match anything
-    // and A is 'true' to get the objects matched at this step, or
-    //   false to have them excluded from the response (always true
-    //   for the last step
-    for (i in Spec) {
-        req.url += encodeURIComponent(Spec[i].bucket||'_')+','+
-            encodeURIComponent(Spec[i].tag||'_')+','+
-            ((Spec[i].acc || i == Spec.length-1) ? '1' : '_')+'/';
-    }
-
-    if (Nocache) req.url += '?nocache='+(+new Date());
-
-    return $.ajax(req);
-}
-
-JiakClient.prototype.setBucketSchema = function(Bucket, Schema, Callback) {
-    if (!('required_fields' in Schema))
-        Schema.required_fields = [];
-
-    if (!('read_mask' in Schema))
-        Schema.read_mask = Schema.allowed_fields;
-
-    if (!('write_mask' in Schema))
-        Schema.write_mask = Schema.read_mask;
-
-    $.ajax({
-        type:        'PUT',
-        url:         this.path(Bucket),
-        contentType: 'application/json',
-        data:        JSON.stringify({schema:Schema}),
-        success:     Callback ? function() { Callback(true); } : undefined,
-        error:       Callback ? function() { Callback(false); } : undefined
-    });
-}
-
-JiakClient.prototype.path = function(Bucket, Key) {
-    var p = this.baseurl;
-    if (Bucket) {
-        p += encodeURIComponent(Bucket)+'/';
-        if (Key)
-            p += encodeURIComponent(Key);
-    }
-    return p;
-}

demo/stickynotes/priv/www/js/jquery-ui-personalized-1.6rc2.min.js

-;(function($){var _remove=$.fn.remove;$.fn.remove=function(){$("*",this).add(this).triggerHandler("remove");return _remove.apply(this,arguments);};function isVisible(element){function checkStyles(element){var style=element.style;return(style.display!='none'&&style.visibility!='hidden');}
-var visible=checkStyles(element);(visible&&$.each($.dir(element,'parentNode'),function(){return(visible=checkStyles(this));}));return visible;}
-$.extend($.expr[':'],{data:function(a,i,m){return $.data(a,m[3]);},tabbable:function(a,i,m){var nodeName=a.nodeName.toLowerCase();return(a.tabIndex>=0&&(('a'==nodeName&&a.href)||(/input|select|textarea|button/.test(nodeName)&&'hidden'!=a.type&&!a.disabled))&&isVisible(a));}});$.keyCode={BACKSPACE:8,CAPS_LOCK:20,COMMA:188,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38};function getter(namespace,plugin,method,args){function getMethods(type){var methods=$[namespace][plugin][type]||[];return(typeof methods=='string'?methods.split(/,?\s+/):methods);}
-var methods=getMethods('getter');if(args.length==1&&typeof args[0]=='string'){methods=methods.concat(getMethods('getterSetter'));}
-return($.inArray(method,methods)!=-1);}
-$.widget=function(name,prototype){var namespace=name.split(".")[0];name=name.split(".")[1];$.fn[name]=function(options){var isMethodCall=(typeof options=='string'),args=Array.prototype.slice.call(arguments,1);if(isMethodCall&&options.substring(0,1)=='_'){return this;}
-if(isMethodCall&&getter(namespace,name,options,args)){var instance=$.data(this[0],name);return(instance?instance[options].apply(instance,args):undefined);}
-return this.each(function(){var instance=$.data(this,name);(!instance&&!isMethodCall&&$.data(this,name,new $[namespace][name](this,options)));(instance&&isMethodCall&&$.isFunction(instance[options])&&instance[options].apply(instance,args));});};$[namespace][name]=function(element,options){var self=this;this.widgetName=name;this.widgetEventPrefix=$[namespace][name].eventPrefix||name;this.widgetBaseClass=namespace+'-'+name;this.options=$.extend({},$.widget.defaults,$[namespace][name].defaults,$.metadata&&$.metadata.get(element)[name],options);this.element=$(element).bind('setData.'+name,function(e,key,value){return self._setData(key,value);}).bind('getData.'+name,function(e,key){return self._getData(key);}).bind('remove',function(){return self.destroy();});this._init();};$[namespace][name].prototype=$.extend({},$.widget.prototype,prototype);$[namespace][name].getterSetter='option';};$.widget.prototype={_init:function(){},destroy:function(){this.element.removeData(this.widgetName);},option:function(key,value){var options=key,self=this;if(typeof key=="string"){if(value===undefined){return this._getData(key);}
-options={};options[key]=value;}
-$.each(options,function(key,value){self._setData(key,value);});},_getData:function(key){return this.options[key];},_setData:function(key,value){this.options[key]=value;if(key=='disabled'){this.element[value?'addClass':'removeClass'](this.widgetBaseClass+'-disabled');}},enable:function(){this._setData('disabled',false);},disable:function(){this._setData('disabled',true);},_trigger:function(type,e,data){var eventName=(type==this.widgetEventPrefix?type:this.widgetEventPrefix+type);e=e||$.event.fix({type:eventName,target:this.element[0]});return this.element.triggerHandler(eventName,[e,data],this.options[type]);}};$.widget.defaults={disabled:false};$.ui={plugin:{add:function(module,option,set){var proto=$.ui[module].prototype;for(var i in set){proto.plugins[i]=proto.plugins[i]||[];proto.plugins[i].push([option,set[i]]);}},call:function(instance,name,args){var set=instance.plugins[name];if(!set){return;}
-for(var i=0;i<set.length;i++){if(instance.options[set[i][0]]){set[i][1].apply(instance.element,args);}}}},cssCache:{},css:function(name){if($.ui.cssCache[name]){return $.ui.cssCache[name];}
-var tmp=$('<div class="ui-gen">').addClass(name).css({position:'absolute',top:'-5000px',left:'-5000px',display:'block'}).appendTo('body');$.ui.cssCache[name]=!!((!(/auto|default/).test(tmp.css('cursor'))||(/^[1-9]/).test(tmp.css('height'))||(/^[1-9]/).test(tmp.css('width'))||!(/none/).test(tmp.css('backgroundImage'))||!(/transparent|rgba\(0, 0, 0, 0\)/).test(tmp.css('backgroundColor'))));try{$('body').get(0).removeChild(tmp.get(0));}catch(e){}
-return $.ui.cssCache[name];},disableSelection:function(el){return $(el).attr('unselectable','on').css('MozUserSelect','none').bind('selectstart.ui',function(){return false;});},enableSelection:function(el){return $(el).attr('unselectable','off').css('MozUserSelect','').unbind('selectstart.ui');},hasScroll:function(e,a){if($(e).css('overflow')=='hidden'){return false;}
-var scroll=(a&&a=='left')?'scrollLeft':'scrollTop',has=false;if(e[scroll]>0){return true;}
-e[scroll]=1;has=(e[scroll]>0);e[scroll]=0;return has;}};$.ui.mouse={_mouseInit:function(){var self=this;this.element.bind('mousedown.'+this.widgetName,function(e){return self._mouseDown(e);});if($.browser.msie){this._mouseUnselectable=this.element.attr('unselectable');this.element.attr('unselectable','on');}
-this.started=false;},_mouseDestroy:function(){this.element.unbind('.'+this.widgetName);($.browser.msie&&this.element.attr('unselectable',this._mouseUnselectable));},_mouseDown:function(e){(this._mouseStarted&&this._mouseUp(e));this._mouseDownEvent=e;var self=this,btnIsLeft=(e.which==1),elIsCancel=(typeof this.options.cancel=="string"?$(e.target).parents().add(e.target).filter(this.options.cancel).length:false);if(!btnIsLeft||elIsCancel||!this._mouseCapture(e)){return true;}
-this.mouseDelayMet=!this.options.delay;if(!this.mouseDelayMet){this._mouseDelayTimer=setTimeout(function(){self.mouseDelayMet=true;},this.options.delay);}
-if(this._mouseDistanceMet(e)&&this._mouseDelayMet(e)){this._mouseStarted=(this._mouseStart(e)!==false);if(!this._mouseStarted){e.preventDefault();return true;}}
-this._mouseMoveDelegate=function(e){return self._mouseMove(e);};this._mouseUpDelegate=function(e){return self._mouseUp(e);};$(document).bind('mousemove.'+this.widgetName,this._mouseMoveDelegate).bind('mouseup.'+this.widgetName,this._mouseUpDelegate);return false;},_mouseMove:function(e){if($.browser.msie&&!e.button){return this._mouseUp(e);}
-if(this._mouseStarted){this._mouseDrag(e);return false;}
-if(this._mouseDistanceMet(e)&&this._mouseDelayMet(e)){this._mouseStarted=(this._mouseStart(this._mouseDownEvent,e)!==false);(this._mouseStarted?this._mouseDrag(e):this._mouseUp(e));}
-return!this._mouseStarted;},_mouseUp:function(e){$(document).unbind('mousemove.'+this.widgetName,this._mouseMoveDelegate).unbind('mouseup.'+this.widgetName,this._mouseUpDelegate);if(this._mouseStarted){this._mouseStarted=false;this._mouseStop(e);}
-return false;},_mouseDistanceMet:function(e){return(Math.max(Math.abs(this._mouseDownEvent.pageX-e.pageX),Math.abs(this._mouseDownEvent.pageY-e.pageY))>=this.options.distance);},_mouseDelayMet:function(e){return this.mouseDelayMet;},_mouseStart:function(e){},_mouseDrag:function(e){},_mouseStop:function(e){},_mouseCapture:function(e){return true;}};$.ui.mouse.defaults={cancel:null,distance:1,delay:0};})(jQuery);(function($){$.widget("ui.draggable",$.extend({},$.ui.mouse,{getHandle:function(e){var handle=!this.options.handle||!$(this.options.handle,this.element).length?true:false;$(this.options.handle,this.element).find("*").andSelf().each(function(){if(this==e.target)handle=true;});return handle;},createHelper:function(){var o=this.options;var helper=$.isFunction(o.helper)?$(o.helper.apply(this.element[0],[e])):(o.helper=='clone'?this.element.clone():this.element);if(!helper.parents('body').length)
-helper.appendTo((o.appendTo=='parent'?this.element[0].parentNode:o.appendTo));if(helper[0]!=this.element[0]&&!(/(fixed|absolute)/).test(helper.css("position")))
-helper.css("position","absolute");return helper;},_init:function(){if(this.options.helper=='original'&&!(/^(?:r|a|f)/).test(this.element.css("position")))
-this.element[0].style.position='relative';(this.options.cssNamespace&&this.element.addClass(this.options.cssNamespace+"-draggable"));(this.options.disabled&&this.element.addClass('ui-draggable-disabled'));this._mouseInit();},_mouseCapture:function(e){var o=this.options;if(this.helper||o.disabled||$(e.target).is('.ui-resizable-handle'))
-return false;this.handle=this.getHandle(e);if(!this.handle)
-return false;return true;},_mouseStart:function(e){var o=this.options;this.helper=this.createHelper();if($.ui.ddmanager)
-$.ui.ddmanager.current=this;this.margins={left:(parseInt(this.element.css("marginLeft"),10)||0),top:(parseInt(this.element.css("marginTop"),10)||0)};this.cssPosition=this.helper.css("position");this.offset=this.element.offset();this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left};this.offset.click={left:e.pageX-this.offset.left,top:e.pageY-this.offset.top};this.cacheScrollParents();this.offsetParent=this.helper.offsetParent();var po=this.offsetParent.offset();if(this.offsetParent[0]==document.body&&$.browser.mozilla)po={top:0,left:0};this.offset.parent={top:po.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:po.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)};if(this.cssPosition=="relative"){var p=this.element.position();this.offset.relative={top:p.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollTopParent.scrollTop(),left:p.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollLeftParent.scrollLeft()};}else{this.offset.relative={top:0,left:0};}
-this.originalPosition=this._generatePosition(e);this.cacheHelperProportions();if(o.cursorAt)
-this.adjustOffsetFromHelper(o.cursorAt);$.extend(this,{PAGEY_INCLUDES_SCROLL:(this.cssPosition=="absolute"&&(!this.scrollTopParent[0].tagName||(/(html|body)/i).test(this.scrollTopParent[0].tagName))),PAGEX_INCLUDES_SCROLL:(this.cssPosition=="absolute"&&(!this.scrollLeftParent[0].tagName||(/(html|body)/i).test(this.scrollLeftParent[0].tagName))),OFFSET_PARENT_NOT_SCROLL_PARENT_Y:this.scrollTopParent[0]!=this.offsetParent[0]&&!(this.scrollTopParent[0]==document&&(/(body|html)/i).test(this.offsetParent[0].tagName)),OFFSET_PARENT_NOT_SCROLL_PARENT_X:this.scrollLeftParent[0]!=this.offsetParent[0]&&!(this.scrollLeftParent[0]==document&&(/(body|html)/i).test(this.offsetParent[0].tagName))});if(o.containment)
-this.setContainment();this._propagate("start",e);this.cacheHelperProportions();if($.ui.ddmanager&&!o.dropBehaviour)
-$.ui.ddmanager.prepareOffsets(this,e);this.helper.addClass("ui-draggable-dragging");this._mouseDrag(e);return true;},cacheScrollParents:function(){this.scrollTopParent=function(el){do{if(/auto|scroll/.test(el.css('overflow'))||(/auto|scroll/).test(el.css('overflow-y')))return el;el=el.parent();}while(el[0].parentNode);return $(document);}(this.helper);this.scrollLeftParent=function(el){do{if(/auto|scroll/.test(el.css('overflow'))||(/auto|scroll/).test(el.css('overflow-x')))return el;el=el.parent();}while(el[0].parentNode);return $(document);}(this.helper);},adjustOffsetFromHelper:function(obj){if(obj.left!=undefined)this.offset.click.left=obj.left+this.margins.left;if(obj.right!=undefined)this.offset.click.left=this.helperProportions.width-obj.right+this.margins.left;if(obj.top!=undefined)this.offset.click.top=obj.top+this.margins.top;if(obj.bottom!=undefined)this.offset.click.top=this.helperProportions.height-obj.bottom+this.margins.top;},cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()};},setContainment:function(){var o=this.options;if(o.containment=='parent')o.containment=this.helper[0].parentNode;if(o.containment=='document'||o.containment=='window')this.containment=[0-this.offset.relative.left-this.offset.parent.left,0-this.offset.relative.top-this.offset.parent.top,$(o.containment=='document'?document:window).width()-this.offset.relative.left-this.offset.parent.left-this.helperProportions.width-this.margins.left-(parseInt(this.element.css("marginRight"),10)||0),($(o.containment=='document'?document:window).height()||document.body.parentNode.scrollHeight)-this.offset.relative.top-this.offset.parent.top-this.helperProportions.height-this.margins.top-(parseInt(this.element.css("marginBottom"),10)||0)];if(!(/^(document|window|parent)$/).test(o.containment)){var ce=$(o.containment)[0];var co=$(o.containment).offset();var over=($(ce).css("overflow")!='hidden');this.containment=[co.left+(parseInt($(ce).css("borderLeftWidth"),10)||0)-this.offset.relative.left-this.offset.parent.left,co.top+(parseInt($(ce).css("borderTopWidth"),10)||0)-this.offset.relative.top-this.offset.parent.top,co.left+(over?Math.max(ce.scrollWidth,ce.offsetWidth):ce.offsetWidth)-(parseInt($(ce).css("borderLeftWidth"),10)||0)-this.offset.relative.left-this.offset.parent.left-this.helperProportions.width-this.margins.left-(parseInt(this.element.css("marginRight"),10)||0),co.top+(over?Math.max(ce.scrollHeight,ce.offsetHeight):ce.offsetHeight)-(parseInt($(ce).css("borderTopWidth"),10)||0)-this.offset.relative.top-this.offset.parent.top-this.helperProportions.height-this.margins.top-(parseInt(this.element.css("marginBottom"),10)||0)];}},_convertPositionTo:function(d,pos){if(!pos)pos=this.position;var mod=d=="absolute"?1:-1;return{top:(pos.top
-+this.offset.relative.top*mod
-+this.offset.parent.top*mod
--(this.cssPosition=="fixed"||this.PAGEY_INCLUDES_SCROLL||this.OFFSET_PARENT_NOT_SCROLL_PARENT_Y?0:this.scrollTopParent.scrollTop())*mod
-+(this.cssPosition=="fixed"?$(document).scrollTop():0)*mod
-+this.margins.top*mod),left:(pos.left
-+this.offset.relative.left*mod
-+this.offset.parent.left*mod
--(this.cssPosition=="fixed"||this.PAGEX_INCLUDES_SCROLL||this.OFFSET_PARENT_NOT_SCROLL_PARENT_X?0:this.scrollLeftParent.scrollLeft())*mod
-+(this.cssPosition=="fixed"?$(document).scrollLeft():0)*mod
-+this.margins.left*mod)};},_generatePosition:function(e){var o=this.options;var position={top:(e.pageY
--this.offset.click.top
--this.offset.relative.top
--this.offset.parent.top
-+(this.cssPosition=="fixed"||this.PAGEY_INCLUDES_SCROLL||this.OFFSET_PARENT_NOT_SCROLL_PARENT_Y?0:this.scrollTopParent.scrollTop())
--(this.cssPosition=="fixed"?$(document).scrollTop():0)),left:(e.pageX
--this.offset.click.left
--this.offset.relative.left
--this.offset.parent.left
-+(this.cssPosition=="fixed"||this.PAGEX_INCLUDES_SCROLL||this.OFFSET_PARENT_NOT_SCROLL_PARENT_X?0:this.scrollLeftParent.scrollLeft())
--(this.cssPosition=="fixed"?$(document).scrollLeft():0))};if(!this.originalPosition)return position;if(this.containment){if(position.left<this.containment[0])position.left=this.containment[0];if(position.top<this.containment[1])position.top=this.containment[1];if(position.left>this.containment[2])position.left=this.containment[2];if(position.top>this.containment[3])position.top=this.containment[3];}
-if(o.grid){var top=this.originalPosition.top+Math.round((position.top-this.originalPosition.top)/o.grid[1])*o.grid[1];position.top=this.containment?(!(top<this.containment[1]||top>this.containment[3])?top:(!(top<this.containment[1])?top-o.grid[1]:top+o.grid[1])):top;var left=this.originalPosition.left+Math.round((position.left-this.originalPosition.left)/o.grid[0])*o.grid[0];position.left=this.containment?(!(left<this.containment[0]||left>this.containment[2])?left:(!(left<this.containment[0])?left-o.grid[0]:left+o.grid[0])):left;}
-return position;},_mouseDrag:function(e){this.position=this._generatePosition(e);this.positionAbs=this._convertPositionTo("absolute");this.position=this._propagate("drag",e)||this.position;if(!this.options.axis||this.options.axis!="y")this.helper[0].style.left=this.position.left+'px';if(!this.options.axis||this.options.axis!="x")this.helper[0].style.top=this.position.top+'px';if($.ui.ddmanager)$.ui.ddmanager.drag(this,e);return false;},_mouseStop:function(e){var dropped=false;if($.ui.ddmanager&&!this.options.dropBehaviour)
-var dropped=$.ui.ddmanager.drop(this,e);if((this.options.revert=="invalid"&&!dropped)||(this.options.revert=="valid"&&dropped)||this.options.revert===true||($.isFunction(this.options.revert)&&this.options.revert.call(this.element,dropped))){var self=this;$(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration,10)||500,function(){self._propagate("stop",e);self._clear();});}else{this._propagate("stop",e);this._clear();}
-return false;},_clear:function(){this.helper.removeClass("ui-draggable-dragging");if(this.options.helper!='original'&&!this.cancelHelperRemoval)this.helper.remove();this.helper=null;this.cancelHelperRemoval=false;},plugins:{},uiHash:function(e){return{helper:this.helper,position:this.position,absolutePosition:this.positionAbs,options:this.options};},_propagate:function(n,e){$.ui.plugin.call(this,n,[e,this.uiHash()]);if(n=="drag")this.positionAbs=this._convertPositionTo("absolute");return this.element.triggerHandler(n=="drag"?n:"drag"+n,[e,this.uiHash()],this.options[n]);},destroy:function(){if(!this.element.data('draggable'))return;this.element.removeData("draggable").unbind(".draggable").removeClass('ui-draggable ui-draggable-dragging ui-draggable-disabled');this._mouseDestroy();}}));$.extend($.ui.draggable,{defaults:{appendTo:"parent",axis:false,cancel:":input",delay:0,distance:1,helper:"original",scope:"default",cssNamespace:"ui"}});$.ui.plugin.add("draggable","cursor",{start:function(e,ui){var t=$('body');if(t.css("cursor"))ui.options._cursor=t.css("cursor");t.css("cursor",ui.options.cursor);},stop:function(e,ui){if(ui.options._cursor)$('body').css("cursor",ui.options._cursor);}});$.ui.plugin.add("draggable","zIndex",{start:function(e,ui){var t=$(ui.helper);if(t.css("zIndex"))ui.options._zIndex=t.css("zIndex");t.css('zIndex',ui.options.zIndex);},stop:function(e,ui){if(ui.options._zIndex)$(ui.helper).css('zIndex',ui.options._zIndex);}});$.ui.plugin.add("draggable","opacity",{start:function(e,ui){var t=$(ui.helper);if(t.css("opacity"))ui.options._opacity=t.css("opacity");t.css('opacity',ui.options.opacity);},stop:function(e,ui){if(ui.options._opacity)$(ui.helper).css('opacity',ui.options._opacity);}});$.ui.plugin.add("draggable","iframeFix",{start:function(e,ui){$(ui.options.iframeFix===true?"iframe":ui.options.iframeFix).each(function(){$('<div class="ui-draggable-iframeFix" style="background: #fff;"></div>').css({width:this.offsetWidth+"px",height:this.offsetHeight+"px",position:"absolute",opacity:"0.001",zIndex:1000}).css($(this).offset()).appendTo("body");});},stop:function(e,ui){$("div.ui-draggable-iframeFix").each(function(){this.parentNode.removeChild(this);});}});$.ui.plugin.add("draggable","scroll",{start:function(e,ui){var o=ui.options;var i=$(this).data("draggable");o.scrollSensitivity=o.scrollSensitivity||20;o.scrollSpeed=o.scrollSpeed||20;i.overflowY=function(el){do{if(/auto|scroll/.test(el.css('overflow'))||(/auto|scroll/).test(el.css('overflow-y')))return el;el=el.parent();}while(el[0].parentNode);return $(document);}(this);i.overflowX=function(el){do{if(/auto|scroll/.test(el.css('overflow'))||(/auto|scroll/).test(el.css('overflow-x')))return el;el=el.parent();}while(el[0].parentNode);return $(document);}(this);if(i.overflowY[0]!=document&&i.overflowY[0].tagName!='HTML')i.overflowYOffset=i.overflowY.offset();if(i.overflowX[0]!=document&&i.overflowX[0].tagName!='HTML')i.overflowXOffset=i.overflowX.offset();},drag:function(e,ui){var o=ui.options,scrolled=false;var i=$(this).data("draggable");if(i.overflowY[0]!=document&&i.overflowY[0].tagName!='HTML'){if((i.overflowYOffset.top+i.overflowY[0].offsetHeight)-e.pageY<o.scrollSensitivity)
-i.overflowY[0].scrollTop=scrolled=i.overflowY[0].scrollTop+o.scrollSpeed;if(e.pageY-i.overflowYOffset.top<o.scrollSensitivity)
-i.overflowY[0].scrollTop=scrolled=i.overflowY[0].scrollTop-o.scrollSpeed;}else{if(e.pageY-$(document).scrollTop()<o.scrollSensitivity)
-scrolled=$(document).scrollTop($(document).scrollTop()-o.scrollSpeed);if($(window).height()-(e.pageY-$(document).scrollTop())<o.scrollSensitivity)
-scrolled=$(document).scrollTop($(document).scrollTop()+o.scrollSpeed);}
-if(i.overflowX[0]!=document&&i.overflowX[0].tagName!='HTML'){if((i.overflowXOffset.left+i.overflowX[0].offsetWidth)-e.pageX<o.scrollSensitivity)
-i.overflowX[0].scrollLeft=scrolled=i.overflowX[0].scrollLeft+o.scrollSpeed;if(e.pageX-i.overflowXOffset.left<o.scrollSensitivity)
-i.overflowX[0].scrollLeft=scrolled=i.overflowX[0].scrollLeft-o.scrollSpeed;}else{if(e.pageX-$(document).scrollLeft()<o.scrollSensitivity)
-scrolled=$(document).scrollLeft($(document).scrollLeft()-o.scrollSpeed);if($(window).width()-(e.pageX-$(document).scrollLeft())<o.scrollSensitivity)
-scrolled=$(document).scrollLeft($(document).scrollLeft()+o.scrollSpeed);}
-if(scrolled!==false)
-$.ui.ddmanager.prepareOffsets(i,e);}});$.ui.plugin.add("draggable","snap",{start:function(e,ui){var inst=$(this).data("draggable");inst.snapElements=[];$(ui.options.snap.constructor!=String?(ui.options.snap.items||':data(draggable)'):ui.options.snap).each(function(){var $t=$(this);var $o=$t.offset();if(this!=inst.element[0])inst.snapElements.push({item:this,width:$t.outerWidth(),height:$t.outerHeight(),top:$o.top,left:$o.left});});},drag:function(e,ui){var inst=$(this).data("draggable");var d=ui.options.snapTolerance||20;var x1=ui.absolutePosition.left,x2=x1+inst.helperProportions.width,y1=ui.absolutePosition.top,y2=y1+inst.helperProportions.height;for(var i=inst.snapElements.length-1;i>=0;i--){var l=inst.snapElements[i].left,r=l+inst.snapElements[i].width,t=inst.snapElements[i].top,b=t+inst.snapElements[i].height;if(!((l-d<x1&&x1<r+d&&t-d<y1&&y1<b+d)||(l-d<x1&&x1<r+d&&t-d<y2&&y2<b+d)||(l-d<x2&&x2<r+d&&t-d<y1&&y1<b+d)||(l-d<x2&&x2<r+d&&t-d<y2&&y2<b+d))){if(inst.snapElements[i].snapping)(inst.options.snap.release&&inst.options.snap.release.call(inst.element,null,$.extend(inst.uiHash(),{snapItem:inst.snapElements[i].item})));inst.snapElements[i].snapping=false;continue;}
-if(ui.options.snapMode!='inner'){var ts=Math.abs(t-y2)<=d;var bs=Math.abs(b-y1)<=d;var ls=Math.abs(l-x2)<=d;var rs=Math.abs(r-x1)<=d;if(ts)ui.position.top=inst._convertPositionTo("relative",{top:t-inst.helperProportions.height,left:0}).top;if(bs)ui.position.top=inst._convertPositionTo("relative",{top:b,left:0}).top;if(ls)ui.position.left=inst._convertPositionTo("relative",{top:0,left:l-inst.helperProportions.width}).left;if(rs)ui.position.left=inst._convertPositionTo("relative",{top:0,left:r}).left;}
-var first=(ts||bs||ls||rs);if(ui.options.snapMode!='outer'){var ts=Math.abs(t-y1)<=d;var bs=Math.abs(b-y2)<=d;var ls=Math.abs(l-x1)<=d;var rs=Math.abs(r-x2)<=d;if(ts)ui.position.top=inst._convertPositionTo("relative",{top:t,left:0}).top;if(bs)ui.position.top=inst._convertPositionTo("relative",{top:b-inst.helperProportions.height,left:0}).top;if(ls)ui.position.left=inst._convertPositionTo("relative",{top:0,left:l}).left;if(rs)ui.position.left=inst._convertPositionTo("relative",{top:0,left:r-inst.helperProportions.width}).left;}
-if(!inst.snapElements[i].snapping&&(ts||bs||ls||rs||first))
-(inst.options.snap.snap&&inst.options.snap.snap.call(inst.element,null,$.extend(inst.uiHash(),{snapItem:inst.snapElements[i].item})));inst.snapElements[i].snapping=(ts||bs||ls||rs||first);};}});$.ui.plugin.add("draggable","connectToSortable",{start:function(e,ui){var inst=$(this).data("draggable");inst.sortables=[];$(ui.options.connectToSortable).each(function(){if($.data(this,'sortable')){var sortable=$.data(this,'sortable');inst.sortables.push({instance:sortable,shouldRevert:sortable.options.revert});sortable._refreshItems();sortable._propagate("activate",e,inst);}});},stop:function(e,ui){var inst=$(this).data("draggable");$.each(inst.sortables,function(){if(this.instance.isOver){this.instance.isOver=0;inst.cancelHelperRemoval=true;this.instance.cancelHelperRemoval=false;if(this.shouldRevert)this.instance.options.revert=true;this.instance._mouseStop(e);this.instance.element.triggerHandler("sortreceive",[e,$.extend(this.instance.ui(),{sender:inst.element})],this.instance.options["receive"]);this.instance.options.helper=this.instance.options._helper;}else{this.instance._propagate("deactivate",e,inst);}});},drag:function(e,ui){var inst=$(this).data("draggable"),self=this;var checkPos=function(o){var l=o.left,r=l+o.width,t=o.top,b=t+o.height;return(l<(this.positionAbs.left+this.offset.click.left)&&(this.positionAbs.left+this.offset.click.left)<r&&t<(this.positionAbs.top+this.offset.click.top)&&(this.positionAbs.top+this.offset.click.top)<b);};$.each(inst.sortables,function(i){if(checkPos.call(inst,this.instance.containerCache)){if(!this.instance.isOver){this.instance.isOver=1;this.instance.currentItem=$(self).clone().appendTo(this.instance.element).data("sortable-item",true);this.instance.options._helper=this.instance.options.helper;this.instance.options.helper=function(){return ui.helper[0];};e.target=this.instance.currentItem[0];this.instance._mouseCapture(e,true);this.instance._mouseStart(e,true,true);this.instance.offset.click.top=inst.offset.click.top;this.instance.offset.click.left=inst.offset.click.left;this.instance.offset.parent.left-=inst.offset.parent.left-this.instance.offset.parent.left;this.instance.offset.parent.top-=inst.offset.parent.top-this.instance.offset.parent.top;inst._propagate("toSortable",e);}
-if(this.instance.currentItem)this.instance._mouseDrag(e);}else{if(this.instance.isOver){this.instance.isOver=0;this.instance.cancelHelperRemoval=true;this.instance.options.revert=false;this.instance._mouseStop(e,true);this.instance.options.helper=this.instance.options._helper;this.instance.currentItem.remove();if(this.instance.placeholder)this.instance.placeholder.remove();inst._propagate("fromSortable",e);}};});}});$.ui.plugin.add("draggable","stack",{start:function(e,ui){var group=$.makeArray($(ui.options.stack.group)).sort(function(a,b){return(parseInt($(a).css("zIndex"),10)||ui.options.stack.min)-(parseInt($(b).css("zIndex"),10)||ui.options.stack.min);});$(group).each(function(i){this.style.zIndex=ui.options.stack.min+i;});this[0].style.zIndex=ui.options.stack.min+group.length;}});})(jQuery);(function($){$.widget("ui.droppable",{_setData:function(key,value){if(key=='accept'){this.options.accept=value&&$.isFunction(value)?value:function(d){return d.is(accept);};}else{$.widget.prototype._setData.apply(this,arguments);}},_init:function(){var o=this.options,accept=o.accept;this.isover=0;this.isout=1;this.options.accept=this.options.accept&&$.isFunction(this.options.accept)?this.options.accept:function(d){return d.is(accept);};this.proportions={width:this.element[0].offsetWidth,height:this.element[0].offsetHeight};$.ui.ddmanager.droppables[this.options.scope]=$.ui.ddmanager.droppables[this.options.scope]||[];$.ui.ddmanager.droppables[this.options.scope].push(this);(this.options.cssNamespace&&this.element.addClass(this.options.cssNamespace+"-droppable"));},plugins:{},ui:function(c){return{draggable:(c.currentItem||c.element),helper:c.helper,position:c.position,absolutePosition:c.positionAbs,options:this.options,element:this.element};},destroy:function(){var drop=$.ui.ddmanager.droppables[this.options.scope];for(var i=0;i<drop.length;i++)
-if(drop[i]==this)
-drop.splice(i,1);this.element.removeClass("ui-droppable-disabled").removeData("droppable").unbind(".droppable");},_over:function(e){var draggable=$.ui.ddmanager.current;if(!draggable||(draggable.currentItem||draggable.element)[0]==this.element[0])return;if(this.options.accept.call(this.element,(draggable.currentItem||draggable.element))){$.ui.plugin.call(this,'over',[e,this.ui(draggable)]);this.element.triggerHandler("dropover",[e,this.ui(draggable)],this.options.over);}},_out:function(e){var draggable=$.ui.ddmanager.current;if(!draggable||(draggable.currentItem||draggable.element)[0]==this.element[0])return;if(this.options.accept.call(this.element,(draggable.currentItem||draggable.element))){$.ui.plugin.call(this,'out',[e,this.ui(draggable)]);this.element.triggerHandler("dropout",[e,this.ui(draggable)],this.options.out);}},_drop:function(e,custom){var draggable=custom||$.ui.ddmanager.current;if(!draggable||(draggable.currentItem||draggable.element)[0]==this.element[0])return false;var childrenIntersection=false;this.element.find(":data(droppable)").not(".ui-draggable-dragging").each(function(){var inst=$.data(this,'droppable');if(inst.options.greedy&&$.ui.intersect(draggable,$.extend(inst,{offset:inst.element.offset()}),inst.options.tolerance)){childrenIntersection=true;return false;}});if(childrenIntersection)return false;if(this.options.accept.call(this.element,(draggable.currentItem||draggable.element))){$.ui.plugin.call(this,'drop',[e,this.ui(draggable)]);this.element.triggerHandler("drop",[e,this.ui(draggable)],this.options.drop);return this.element;}
-return false;},_activate:function(e){var draggable=$.ui.ddmanager.current;$.ui.plugin.call(this,'activate',[e,this.ui(draggable)]);if(draggable)this.element.triggerHandler("dropactivate",[e,this.ui(draggable)],this.options.activate);},_deactivate:function(e){var draggable=$.ui.ddmanager.current;$.ui.plugin.call(this,'deactivate',[e,this.ui(draggable)]);if(draggable)this.element.triggerHandler("dropdeactivate",[e,this.ui(draggable)],this.options.deactivate);}});$.extend($.ui.droppable,{defaults:{disabled:false,tolerance:'intersect',scope:'default',cssNamespace:'ui'}});$.ui.intersect=function(draggable,droppable,toleranceMode){if(!droppable.offset)return false;var x1=(draggable.positionAbs||draggable.position.absolute).left,x2=x1+draggable.helperProportions.width,y1=(draggable.positionAbs||draggable.position.absolute).top,y2=y1+draggable.helperProportions.height;var l=droppable.offset.left,r=l+droppable.proportions.width,t=droppable.offset.top,b=t+droppable.proportions.height;switch(toleranceMode){case'fit':return(l<x1&&x2<r&&t<y1&&y2<b);break;case'intersect':return(l<x1+(draggable.helperProportions.width/2)&&x2-(draggable.helperProportions.width/2)<r&&t<y1+(draggable.helperProportions.height/2)&&y2-(draggable.helperProportions.height/2)<b);break;case'pointer':return(l<((draggable.positionAbs||draggable.position.absolute).left+(draggable.clickOffset||draggable.offset.click).left)&&((draggable.positionAbs||draggable.position.absolute).left+(draggable.clickOffset||draggable.offset.click).left)<r&&t<((draggable.positionAbs||draggable.position.absolute).top+(draggable.clickOffset||draggable.offset.click).top)&&((draggable.positionAbs||draggable.position.absolute).top+(draggable.clickOffset||draggable.offset.click).top)<b);break;case'touch':return((y1>=t&&y1<=b)||(y2>=t&&y2<=b)||(y1<t&&y2>b))&&((x1>=l&&x1<=r)||(x2>=l&&x2<=r)||(x1<l&&x2>r));break;default:return false;break;}};$.ui.ddmanager={current:null,droppables:{'default':[]},prepareOffsets:function(t,e){var m=$.ui.ddmanager.droppables[t.options.scope];var type=e?e.type:null;var list=(t.currentItem||t.element).find(":data(droppable)").andSelf();droppablesLoop:for(var i=0;i<m.length;i++){if(m[i].options.disabled||(t&&!m[i].options.accept.call(m[i].element,(t.currentItem||t.element))))continue;for(var j=0;j<list.length;j++){if(list[j]==m[i].element[0]){m[i].proportions.height=0;continue droppablesLoop;}};m[i].visible=m[i].element.css("display")!="none";if(!m[i].visible)continue;m[i].offset=m[i].element.offset();m[i].proportions={width:m[i].element[0].offsetWidth,height:m[i].element[0].offsetHeight};if(type=="dragstart"||type=="sortactivate")m[i]._activate.call(m[i],e);}},drop:function(draggable,e){var dropped=false;$.each($.ui.ddmanager.droppables[draggable.options.scope],function(){if(!this.options)return;if(!this.options.disabled&&this.visible&&$.ui.intersect(draggable,this,this.options.tolerance))
-dropped=this._drop.call(this,e);if(!this.options.disabled&&this.visible&&this.options.accept.call(this.element,(draggable.currentItem||draggable.element))){this.isout=1;this.isover=0;this._deactivate.call(this,e);}});return dropped;},drag:function(draggable,e){if(draggable.options.refreshPositions)$.ui.ddmanager.prepareOffsets(draggable,e);$.each($.ui.ddmanager.droppables[draggable.options.scope],function(){if(this.options.disabled||this.greedyChild||!this.visible)return;var intersects=$.ui.intersect(draggable,this,this.options.tolerance);var c=!intersects&&this.isover==1?'isout':(intersects&&this.isover==0?'isover':null);if(!c)return;var parentInstance;if(this.options.greedy){var parent=this.element.parents(':data(droppable):eq(0)');if(parent.length){parentInstance=$.data(parent[0],'droppable');parentInstance.greedyChild=(c=='isover'?1:0);}}
-if(parentInstance&&c=='isover'){parentInstance['isover']=0;parentInstance['isout']=1;parentInstance._out.call(parentInstance,e);}
-this[c]=1;this[c=='isout'?'isover':'isout']=0;this[c=="isover"?"_over":"_out"].call(this,e);if(parentInstance&&c=='isout'){parentInstance['isout']=0;parentInstance['isover']=1;parentInstance._over.call(parentInstance,e);}});}};$.ui.plugin.add("droppable","activeClass",{activate:function(e,ui){$(this).addClass(ui.options.activeClass);},deactivate:function(e,ui){$(this).removeClass(ui.options.activeClass);},drop:function(e,ui){$(this).removeClass(ui.options.activeClass);}});$.ui.plugin.add("droppable","hoverClass",{over:function(e,ui){$(this).addClass(ui.options.hoverClass);},out:function(e,ui){$(this).removeClass(ui.options.hoverClass);},drop:function(e,ui){$(this).removeClass(ui.options.hoverClass);}});})(jQuery);

demo/stickynotes/priv/www/js/json2.js

-/*
-    http://www.JSON.org/json2.js
-    2008-07-15
-
-    Public Domain.
-
-    NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
-
-    See http://www.JSON.org/js.html
-
-    This file creates a global JSON object containing two methods: stringify
-    and parse.
-
-        JSON.stringify(value, replacer, space)
-            value       any JavaScript value, usually an object or array.
-
-            replacer    an optional parameter that determines how object
-                        values are stringified for objects. It can be a
-                        function or an array.
-
-            space       an optional parameter that specifies the indentation
-                        of nested structures. If it is omitted, the text will
-                        be packed without extra whitespace. If it is a number,
-                        it will specify the number of spaces to indent at each
-                        level. If it is a string (such as '\t' or '&nbsp;'),
-                        it contains the characters used to indent at each level.
-
-            This method produces a JSON text from a JavaScript value.
-
-            When an object value is found, if the object contains a toJSON
-            method, its toJSON method will be called and the result will be
-            stringified. A toJSON method does not serialize: it returns the
-            value represented by the name/value pair that should be serialized,
-            or undefined if nothing should be serialized. The toJSON method
-            will be passed the key associated with the value, and this will be
-            bound to the object holding the key.
-
-            For example, this would serialize Dates as ISO strings.
-
-                Date.prototype.toJSON = function (key) {
-                    function f(n) {
-                        // Format integers to have at least two digits.
-                        return n < 10 ? '0' + n : n;
-                    }
-
-                    return this.getUTCFullYear()   + '-' +
-                         f(this.getUTCMonth() + 1) + '-' +
-                         f(this.getUTCDate())      + 'T' +
-                         f(this.getUTCHours())     + ':' +
-                         f(this.getUTCMinutes())   + ':' +
-                         f(this.getUTCSeconds())   + 'Z';
-                };
-
-            You can provide an optional replacer method. It will be passed the
-            key and value of each member, with this bound to the containing
-            object. The value that is returned from your method will be
-            serialized. If your method returns undefined, then the member will
-            be excluded from the serialization.
-
-            If the replacer parameter is an array, then it will be used to
-            select the members to be serialized. It filters the results such
-            that only members with keys listed in the replacer array are
-            stringified.
-
-            Values that do not have JSON representations, such as undefined or
-            functions, will not be serialized. Such values in objects will be
-            dropped; in arrays they will be replaced with null. You can use
-            a replacer function to replace those with JSON values.
-            JSON.stringify(undefined) returns undefined.
-
-            The optional space parameter produces a stringification of the
-            value that is filled with line breaks and indentation to make it
-            easier to read.
-
-            If the space parameter is a non-empty string, then that string will
-            be used for indentation. If the space parameter is a number, then
-            the indentation will be that many spaces.
-
-            Example:
-
-            text = JSON.stringify(['e', {pluribus: 'unum'}]);
-            // text is '["e",{"pluribus":"unum"}]'
-
-
-            text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
-            // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
-
-            text = JSON.stringify([new Date()], function (key, value) {
-                return this[key] instanceof Date ?
-                    'Date(' + this[key] + ')' : value;
-            });
-            // text is '["Date(---current time---)"]'
-
-
-        JSON.parse(text, reviver)
-            This method parses a JSON text to produce an object or array.
-            It can throw a SyntaxError exception.
-
-            The optional reviver parameter is a function that can filter and
-            transform the results. It receives each of the keys and values,
-            and its return value is used instead of the original value.
-            If it returns what it received, then the structure is not modified.
-            If it returns undefined then the member is deleted.
-
-            Example:
-
-            // Parse the text. Values that look like ISO date strings will
-            // be converted to Date objects.
-
-            myData = JSON.parse(text, function (key, value) {
-                var a;
-                if (typeof value === 'string') {
-                    a =
-/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
-                    if (a) {
-                        return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
-                            +a[5], +a[6]));
-                    }
-                }
-                return value;
-            });
-
-            myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
-                var d;
-                if (typeof value === 'string' &&
-                        value.slice(0, 5) === 'Date(' &&
-                        value.slice(-1) === ')') {
-                    d = new Date(value.slice(5, -1));
-                    if (d) {
-                        return d;
-                    }
-                }
-                return value;
-            });
-
-
-    This is a reference implementation. You are free to copy, modify, or
-    redistribute.
-
-    This code should be minified before deployment.
-    See http://javascript.crockford.com/jsmin.html
-
-    USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
-    NOT CONTROL.
-*/
-
-/*jslint evil: true */
-
-/*global JSON */
-
-/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", call,
-    charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, getUTCMinutes,
-    getUTCMonth, getUTCSeconds, hasOwnProperty, join, lastIndex, length,
-    parse, propertyIsEnumerable, prototype, push, replace, slice, stringify,
-    test, toJSON, toString
-*/
-
-if (!this.JSON) {
-
-// Create a JSON object only if one does not already exist. We create the
-// object in a closure to avoid creating global variables.
-
-    JSON = function () {
-
-        function f(n) {
-            // Format integers to have at least two digits.
-            return n < 10 ? '0' + n : n;
-        }
-
-        Date.prototype.toJSON = function (key) {
-
-            return this.getUTCFullYear()   + '-' +
-                 f(this.getUTCMonth() + 1) + '-' +
-                 f(this.getUTCDate())      + 'T' +
-                 f(this.getUTCHours())     + ':' +
-                 f(this.getUTCMinutes())   + ':' +
-                 f(this.getUTCSeconds())   + 'Z';
-        };
-
-        String.prototype.toJSON =
-        Number.prototype.toJSON =
-        Boolean.prototype.toJSON = function (key) {
-            return this.valueOf();
-        };
-
-        var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
-            escapeable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
-            gap,
-            indent,
-            meta = {    // table of character substitutions
-                '\b': '\\b',
-                '\t': '\\t',
-                '\n': '\\n',
-                '\f': '\\f',
-                '\r': '\\r',
-                '"' : '\\"',
-                '\\': '\\\\'
-            },
-            rep;
-
-
-        function quote(string) {
-
-// If the string contains no control characters, no quote characters, and no
-// backslash characters, then we can safely slap some quotes around it.
-// Otherwise we must also replace the offending characters with safe escape
-// sequences.
-
-            escapeable.lastIndex = 0;
-            return escapeable.test(string) ?
-                '"' + string.replace(escapeable, function (a) {
-                    var c = meta[a];
-                    if (typeof c === 'string') {
-                        return c;
-                    }
-                    return '\\u' + ('0000' +
-                            (+(a.charCodeAt(0))).toString(16)).slice(-4);
-                }) + '"' :
-                '"' + string + '"';
-        }
-
-
-        function str(key, holder) {
-
-// Produce a string from holder[key].
-
-            var i,          // The loop counter.
-                k,          // The member key.
-                v,          // The member value.
-                length,
-                mind = gap,
-                partial,
-                value = holder[key];
-
-// If the value has a toJSON method, call it to obtain a replacement value.
-
-            if (value && typeof value === 'object' &&
-                    typeof value.toJSON === 'function') {
-                value = value.toJSON(key);
-            }
-
-// If we were called with a replacer function, then call the replacer to
-// obtain a replacement value.
-
-            if (typeof rep === 'function') {
-                value = rep.call(holder, key, value);
-            }
-
-// What happens next depends on the value's type.
-
-            switch (typeof value) {
-            case 'string':
-                return quote(value);
-
-            case 'number':
-
-// JSON numbers must be finite. Encode non-finite numbers as null.
-
-                return isFinite(value) ? String(value) : 'null';
-
-            case 'boolean':
-            case 'null':
-
-// If the value is a boolean or null, convert it to a string. Note:
-// typeof null does not produce 'null'. The case is included here in
-// the remote chance that this gets fixed someday.
-
-                return String(value);
-
-// If the type is 'object', we might be dealing with an object or an array or
-// null.
-
-            case 'object':
-
-// Due to a specification blunder in ECMAScript, typeof null is 'object',
-// so watch out for that case.
-
-                if (!value) {
-                    return 'null';
-                }
-
-// Make an array to hold the partial results of stringifying this object value.
-
-                gap += indent;
-                partial = [];
-
-// If the object has a dontEnum length property, we'll treat it as an array.
-
-                if (typeof value.length === 'number' &&
-                        !(value.propertyIsEnumerable('length'))) {
-
-// The object is an array. Stringify every element. Use null as a placeholder
-// for non-JSON values.
-
-                    length = value.length;
-                    for (i = 0; i < length; i += 1) {
-                        partial[i] = str(i, value) || 'null';
-                    }
-
-// Join all of the elements together, separated with commas, and wrap them in
-// brackets.
-
-                    v = partial.length === 0 ? '[]' :
-                        gap ? '[\n' + gap +
-                                partial.join(',\n' + gap) + '\n' +
-                                    mind + ']' :
-                              '[' + partial.join(',') + ']';
-                    gap = mind;
-                    return v;
-                }
-
-// If the replacer is an array, use it to select the members to be stringified.
-
-                if (rep && typeof rep === 'object') {
-                    length = rep.length;
-                    for (i = 0; i < length; i += 1) {
-                        k = rep[i];
-                        if (typeof k === 'string') {
-                            v = str(k, value);
-                            if (v) {
-                                partial.push(quote(k) + (gap ? ': ' : ':') + v);
-                            }
-                        }
-                    }
-                } else {
-
-// Otherwise, iterate through all of the keys in the object.
-
-                    for (k in value) {
-                        if (Object.hasOwnProperty.call(value, k)) {
-                            v = str(k, value);
-                            if (v) {
-                                partial.push(quote(k) + (gap ? ': ' : ':') + v);
-                            }
-                        }
-                    }
-                }
-
-// Join all of the member texts together, separated with commas,
-// and wrap them in braces.
-
-                v = partial.length === 0 ? '{}' :
-                    gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
-                            mind + '}' : '{' + partial.join(',') + '}';
-                gap = mind;
-                return v;
-            }
-        }
-
-// Return the JSON object containing the stringify and parse methods.
-
-        return {
-            stringify: function (value, replacer, space) {
-
-// The stringify method takes a value and an optional replacer, and an optional
-// space parameter, and returns a JSON text. The replacer can be a function
-// that can replace values, or an array of strings that will select the keys.
-// A default replacer method can be provided. Use of the space parameter can
-// produce text that is more easily readable.
-
-                var i;
-                gap = '';
-                indent = '';
-
-// If the space parameter is a number, make an indent string containing that
-// many spaces.
-
-                if (typeof space === 'number') {
-                    for (i = 0; i < space; i += 1) {
-                        indent += ' ';
-                    }
-
-// If the space parameter is a string, it will be used as the indent string.
-
-                } else if (typeof space === 'string') {
-                    indent = space;
-                }
-
-// If there is a replacer, it must be a function or an array.
-// Otherwise, throw an error.
-
-                rep = replacer;
-                if (replacer && typeof replacer !== 'function' &&
-                        (typeof replacer !== 'object' ||
-                         typeof replacer.length !== 'number')) {
-                    throw new Error('JSON.stringify');
-                }
-
-// Make a fake root object containing our value under the key of ''.
-// Return the result of stringifying the value.
-
-                return str('', {'': value});
-            },
-
-
-            parse: function (text, reviver) {
-
-// The parse method takes a text and an optional reviver function, and returns
-// a JavaScript value if the text is a valid JSON text.
-
-                var j;
-
-                function walk(holder, key) {
-
-// The walk method is used to recursively walk the resulting structure so
-// that modifications can be made.
-
-                    var k, v, value = holder[key];
-                    if (value && typeof value === 'object') {
-                        for (k in value) {
-                            if (Object.hasOwnProperty.call(value, k)) {
-                                v = walk(value, k);
-                                if (v !== undefined) {
-                                    value[k] = v;
-                                } else {
-                                    delete value[k];
-                                }
-                            }
-                        }
-                    }
-                    return reviver.call(holder, key, value);
-                }
-
-
-// Parsing happens in four stages. In the first stage, we replace certain
-// Unicode characters with escape sequences. JavaScript handles many characters
-// incorrectly, either silently deleting them, or treating them as line endings.
-
-                cx.lastIndex = 0;
-                if (cx.test(text)) {
-                    text = text.replace(cx, function (a) {
-                        return '\\u' + ('0000' +
-                                (+(a.charCodeAt(0))).toString(16)).slice(-4);
-                    });
-                }
-
-// In the second stage, we run the text against regular expressions that look
-// for non-JSON patterns. We are especially concerned with '()' and 'new'
-// because they can cause invocation, and '=' because it can cause mutation.
-// But just to be safe, we want to reject all unexpected forms.
-
-// We split the second stage into 4 regexp operations in order to work around
-// crippling inefficiencies in IE's and Safari's regexp engines. First we
-// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
-// replace all simple value tokens with ']' characters. Third, we delete all
-// open brackets that follow a colon or comma or that begin the text. Finally,
-// we look to see that the remaining characters are only whitespace or ']' or
-// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
-
-                if (/^[\],:{}\s]*$/.
-test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
-replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
-replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
-
-// In the third stage we use the eval function to compile the text into a
-// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
-// in JavaScript: it can begin a block or an object literal. We wrap the text
-// in parens to eliminate the ambiguity.
-
-                    j = eval('(' + text + ')');
-
-// In the optional fourth stage, we recursively walk the new structure, passing
-// each name/value pair to a reviver function for possible transformation.
-
-                    return typeof reviver === 'function' ?
-                        walk({'': j}, '') : j;
-                }
-
-// If the text is not JSON parseable, then a SyntaxError is thrown.
-
-                throw new SyntaxError('JSON.parse');
-            }
-        };
-    }();
-}

demo/stickynotes/riak-config.erlenv

-{cluster_name, "default"}.
-{ring_state_dir, "priv/ringstate"}.
-{ring_creation_size, 16}.
-{gossip_interval, 60000}.
-{storage_backend, riak_ets_backend}.
-{riak_cookie, stickynotes_cookie}.
-{riak_heart_command, "(cd $RIAK_HOME; ./start-restart.sh $RIAK_HOME/demo/stickynotes/riak-config.erlenv)"}.
-{riak_nodename, riak}.
-{riak_hostname, "127.0.0.1"}.
-{add_paths, ["demo/stickynotes/ebin"]}.
-
-{jiak_name, "jiak"}.
-{riak_web_ip, "127.0.0.1"}.
-{riak_web_port, 8098}.
-{riak_web_logdir, "priv/weblogs"}.
-{jiak_buckets, [notes, groups]}. %% just to get atoms loaded in node
-

demo/stickynotes/src/groups.erl

-%% @doc groups is the handler for the "groups" Riak bucket, which
-%%      conforms to the Jiak protocol
--module(groups).
-
--export([init/2,
-         auth_ok/3,
-         bucket_listable/0,
-         allowed_fields/0,
-         required_fields/0,
-         read_mask/0,
-         write_mask/0,
-         expires_in_seconds/3,
-         check_write/4,
-         effect_write/4,
-         after_write/4]).
--export([merge_siblings/1]).
-
-%% @spec init(jiak_resource:key(), jiak_context()) -> {ok, jiak_context()}
-%% @doc initialize Context for use in this module
-init(_Key, Context) ->
-    {ok, Context}.
-
-%% @spec auth_ok(jiak_resource:key(), webmachine:wrq(), jiak_context()) ->
-%%         {true|webmachine:auth_header(),
-%%          webmachine:wrq(),
-%%          jiak_context()}
-%% @doc This function should behave exactly as the is_authorized/2
-%%      function of any Webmachine resource would behave.
-auth_ok(_Key, ReqData, Context) ->
-    {true, ReqData, Context}.
-
-%% @spec bucket_listable() -> boolean()
-%% @doc Return 'true' if you want clients to be able to request the
-%%      list of keys in this bucket through jiak_resource.  Return
-%%      'false' if the keylist should not be client-visible.
-bucket_listable() -> true.
-
-%% @spec allowed_fields() -> [binary()]
-%% @doc Return a list of field names that are allowed to exist
-%%      in objects of this type.
-allowed_fields() ->
-    [<<"name">>].
-
-%% @spec required_fields() -> [binary()]
-%% @doc Return a list of field names that must exist in a valid
-%%      object of this type
-required_fields() -> allowed_fields().
-
-%% @spec read_mask() -> [binary()]
-%% @doc Return a list of fields that a client using jiak_resource
-%%      should see.  Fields not in this list will be removed from
-%%      the object before sending it to the client.
-read_mask() -> allowed_fields().
-
-%% @spec write_mask() -> [binary()]
-%% @doc Return a list of fields that a client may change through
-%%      jiak_resource.  Edits made by a client to fields that are
-%%      not in this list will generate an error.
-write_mask() -> allowed_fields().
-
-%% @spec expires_in_seconds(jiak_resource:key(),
-%%                          webmachine:wrq(),
-%%                          jiak_context()) ->
-%%          {integer(), webmachine:wrq(), jiak_context()}
-%% @doc Return the number of seconds a client should be allowed to
-%%      cache an object of this type.  This is very similar to the
-%%      expires/2 function of a Webmachine resource, except that it
-%%      returns a number of seconds instead of a datetime.
-expires_in_seconds(_Key, ReqData, Context) ->
-    {600, ReqData, Context}.
-
-%% @spec check_write({container|item, riak_object:binary_key()},
-%%                   jiak_object(), webmachine:wrq(), jiak_context()) ->
-%%         {{ok, jiak_object()}|{error, term()},
-%%          webmachine:wrq(), jiak_context()}
-%% @doc Decide whether or not a write should be allowed.  This
-%%      function should check the validity of JiakObject and/or the
-%%      set of diffs in Context, then return a tuple including {ok, J}
-%%      if the write should be allowed or {error, Reason} if it should
-%%      not be.  The returned JiakObject may be the same JiakObject
-%%      passed in, or a modified one.
-check_write({_PutType, _Key}, JiakObject, ReqData, Context) ->
-    {ObjDiffs,_} = Context:diff(),
-    case lists:foldl(fun check_diff/2, [], ObjDiffs) of
-        [] ->
-            {{ok, JiakObject}, ReqData, Context};
-        Errors ->
-            {{error, list_to_binary(string:join(Errors, ", "))},
-             ReqData, Context}
-    end.
-
-check_diff({<<"name">>, _, Value}, ErrorAcc) ->
-    if is_binary(Value) -> ErrorAcc;
-       true             -> ["name field must be a string"|ErrorAcc]
-    end.
-
-%% @spec effect_write(jiak_resource:key(), jiak_object(),
-%%                    webmachine:wrq(), jiak_context()) ->
-%%         {{ok, jiak_object()}|{error, term()},
-%%          webmachine:wrq(), jiak_context()}
-%% @doc It has been determined that JiakObject is valid, and it will
-%%      be written - this function is an opportunity to act on that
-%%      information before the write happens.
-effect_write(_Key, JiakObject, ReqData, Context) ->
-    {{ok, JiakObject}, ReqData, Context}.
-
-%% @spec after_write(jiak_resource:key(), jiak_object(),
-%%                   webmachine:wrq(), jiak_context()) ->
-%%         {ok, webmachine:wrq(), jiak_context()}
-%% @doc JiakObject has been stored in riak - this function is an
-%%      opportunity to act on that information.
-after_write(_Key, _JiakObject, ReqData, Context) ->
-    {ok, ReqData, Context}.
-
-%% @spec merge_siblings([{Metadata::dict(),
-%%                        {Object::mochijson2:struct(),
-%%                         Links::[jiak_object:link()]}}]) ->
-%%         {dict(),{mochijson2:struct(), [jiak_object:link()]}}
-%% @doc Merge Riak-sibling jiak objects.  Siblings are passed in
-%%      in the form {RiakObjectMetadata,{JiakObjectData,JiakLinks}},
-%%      and this functino should return exactly one structure of
-%%      the same shape.
-merge_siblings(Siblings) ->
-    jiak:standard_sibling_merge(Siblings).

demo/stickynotes/src/jiak_proxy_ibrowse.erl

-%% @author Bryan Fink
-%% @doc jiak_proxy_ibrowse is intended to be a simple webmachine
-%%      resource for proxying Webmachine requests to Jiak.  In theory,
-%%      it's general enough to be a simple proxy to most any other
-%%      HTTP service, but I make no guarantees about assumption it
-%%      makes that are Jiak-specific.
-%%
-%%      This resource performs the same task as jiak_proxy, but uses
-%%      ibrowse instead of inets http.
-%%
-%%      Load this with a dispatch line like:
-%%      {['*'], jiak_proxy_ibrowse, {ExternalPath, JiakPath}}.
-%%      Where:
-%%        ExternalPath is the base path to this resource, like
-%%          "http://localhost:8000/"
-%%        JiakPath is the base path to your Jiak server, like
-%%          "http://localhost:8098/"
-%%
-%%      Another useful example might be:
-%%        {["jiak", '*'], jiak_proxy_ibrowse,
-%%         {"http://localhost:8000/jiak/",
-%%          "http://localhost:8098/"}}
-%%      Which would redirect requests from
-%%        http://localhost:8000/jiak/BUCKET/KEY
-%%      to
-%%        http://localhost:8098/BUCKET/KEY
--module(jiak_proxy_ibrowse).
--export([init/1,
-         service_available/2]).
--include_lib("webmachine/include/webmachine.hrl").
-
-init(Config) -> {ok, Config}.
-
-%% request to Jiak is made in service_available, such that
-%% if Jiak isn't up, we return 503 Service Unavailable, as expected
-service_available(RP, C={_ExternalPath, JiakPath}) ->
-    %% point path at Jiak server
-    Path = lists:append(
-             [JiakPath,
-              wrq:disp_path(RP),
-              case wrq:req_qs(RP) of
-                  [] -> [];
-                  Qs -> [$?|mochiweb_util:urlencode(Qs)]
-              end]),
-
-    %% translate webmachine details to ibrowse details
-    Headers = clean_request_headers(
-                mochiweb_headers:to_list(wrq:req_headers(RP))),
-    Method = wm_to_ibrowse_method(wrq:method(RP)),
-    ReqBody = case wrq:req_body(RP) of
-                  undefined -> [];
-                  B -> B
-              end,
-
-    case ibrowse:send_req(Path, Headers, Method, ReqBody) of
-        {ok, Status, JiakHeaders, RespBody} ->
-            RespHeaders = fix_location(JiakHeaders, C),
-
-            %% stop resource processing here and return whatever
-            %% Jiak wanted to return
-            {{halt, list_to_integer(Status)},
-             wrq:set_resp_headers(RespHeaders,
-                                  wrq:set_resp_body(RespBody, RP)),
-             C};
-        _ ->
-            {false, RP, C}
-    end.
-
-%% ibrowse will recalculate Host and Content-Length headers,
-%% and will muck them up if they're manually specified
-clean_request_headers(Headers) ->
-    [{K,V} || {K,V} <- Headers,
-              K /= 'Host', K /= 'Content-Length'].
-
-%% webmachine expresses method as all-caps string or atom,
-%% while ibrowse uses all-lowercase atom
-wm_to_ibrowse_method(Method) when is_list(Method) ->
-    list_to_atom(string:to_lower(Method));
-wm_to_ibrowse_method(Method) when is_atom(Method) ->
-    wm_to_ibrowse_method(atom_to_list(Method)).
-
-%% Jiak returns a fully-qualified URI in Location -
-%% hack off the Jiak host, and drop in this proxy host
-fix_location([], _) -> [];
-fix_location([{"Location", JiakDataPath}|Rest],
-             {ExternalPath, JiakPath}) ->
-    DataPath = lists:nthtail(length(JiakPath), JiakDataPath),
-    [{"Location", ExternalPath++DataPath}|Rest];
-fix_location([H|T], C) ->
-    [H|fix_location(T, C)].

demo/stickynotes/src/jiak_proxy_inets.erl

-%% @author Bryan Fink
-%% @doc jiak_proxy_inets is intended to be a simple webmachine
-%%      resource for proxying Webmachine requests to Jiak.
-%%
-%%      Note: this is not a production-quality proxy resource.
-%%      For something that will be used in a real application, it
-%%      is recommended that ibrowse be used instead of the inets
-%%      http client.
--module(jiak_proxy_inets).
--export([init/1,
-         service_available/2]).
--include_lib("webmachine/include/webmachine.hrl").
-
-init(Config) -> {ok, Config}.
-
-%% request to jiak is made in service_available, such that
-%% if jiak isn't up, we return 503 Service Unavailable, as expected
-service_available(RP, C={_ExternalPath, JiakPath}) ->
-    %% point path at jiak server
-    Path = lists:append(
-             [JiakPath,
-              wrq:disp_path(RP),
-              case wrq:req_qs(RP) of
-                  [] -> [];
-                  Qs -> [$?|mochiweb_util:urlencode(Qs)]
-              end]),
-
-    %% translate webmachine details to ibrowse details
-    Headers = clean_request_headers(
-                mochiweb_headers:to_list(wrq:req_headers(RP))),
-    Method = wm_to_http_method(wrq:method(RP)),
-    Req = case wrq:req_body(RP) of
-              Empty when Empty==undefined;
-                         Empty==[];
-                         Empty==<<>> ->
-                  {Path, Headers};
-              B ->
-                  {Path, Headers,
-                   wrq:get_req_header("content-type", RP), B}
-          end,
-    case http:request(Method, Req, [{autoredirect, false}], []) of
-        {ok, {{_, Status, _}, JiakHeaders, RespBody}} ->
-            RespHeaders = fix_location(JiakHeaders, C),
-
-            %% stop resource processing here and return whatever
-            %% jiak wanted to return
-            {{halt, Status},
-             wrq:set_resp_headers(RespHeaders,
<