pakyow 0.10.2 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +13 -0
- data/LICENSE +1 -1
- data/README.md +4 -1
- data/bin/pakyow +2 -16
- data/lib/generators/pakyow/app/app_generator.rb +46 -13
- data/lib/generators/pakyow/app/templates/Gemfile +8 -7
- data/lib/generators/pakyow/app/templates/README.md +2 -2
- data/lib/generators/pakyow/app/templates/app/setup.rb +10 -19
- data/lib/generators/pakyow/app/templates/app/views/_templates/default.html +3 -1
- data/lib/generators/pakyow/app/templates/env +1 -0
- data/lib/generators/pakyow/app/templates/env.example +3 -0
- data/lib/generators/pakyow/app/templates/gitignore +8 -0
- data/lib/generators/pakyow/app/templates/public/scripts/ring/components/fastlink.js +3 -2
- data/lib/generators/pakyow/app/templates/public/scripts/ring/components/fastlink.min.js +1 -1
- data/lib/generators/pakyow/app/templates/public/scripts/ring/components/ga.js +34 -0
- data/lib/generators/pakyow/app/templates/public/scripts/ring/components/ga.min.js +1 -0
- data/lib/generators/pakyow/app/templates/public/scripts/ring/components/modal.js +1 -1
- data/lib/generators/pakyow/app/templates/public/scripts/ring/components/modal.min.js +1 -1
- data/lib/generators/pakyow/app/templates/public/scripts/ring/components/mutable.js +5 -1
- data/lib/generators/pakyow/app/templates/public/scripts/ring/components/mutable.min.js +1 -1
- data/lib/generators/pakyow/app/templates/public/scripts/ring/components/navigator.js +10 -6
- data/lib/generators/pakyow/app/templates/public/scripts/ring/components/navigator.min.js +1 -1
- data/lib/generators/pakyow/app/templates/public/scripts/ring/pakyow.js +175 -45
- data/lib/generators/pakyow/app/templates/public/scripts/ring/pakyow.min.js +1 -1
- data/lib/generators/pakyow/app/templates/rspec +3 -0
- data/lib/pakyow.rb +1 -1
- data/lib/pakyow/command_line_interface.rb +79 -0
- data/lib/pakyow/commands/console.rb +27 -0
- data/lib/pakyow/commands/console_methods.rb +10 -0
- data/lib/pakyow/commands/server.rb +38 -0
- data/lib/pakyow/version.rb +3 -0
- metadata +58 -27
- data/lib/commands/USAGE +0 -9
- data/lib/commands/USAGE-CONSOLE +0 -12
- data/lib/commands/USAGE-NEW +0 -11
- data/lib/commands/USAGE-SERVER +0 -16
- data/lib/commands/console.rb +0 -17
- data/lib/commands/server.rb +0 -27
- data/lib/version.rb +0 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 199c2331bc67c6b913a5408523fc60019770d551
|
4
|
+
data.tar.gz: b6e2c05f9d55ca795ffb44cf9ed0f7a21c22837a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1f0bfd078e041e64094ecbba209afdf4b761d760afcabe795b0b83363f112edc20ac1af5167ce63ad5bae5ab429f0dc683747d7db843a6e09fc7c4b0b60e2c46
|
7
|
+
data.tar.gz: 8191df207e1ec7e2211dbe8252c714c7b62b7a078b6cefa88c85ed493686030b1720c9d47b9f30f220faa98443afbe8fb2b88638c987f1796b0502a1b3266926
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,16 @@
|
|
1
|
+
# 0.11.0
|
2
|
+
|
3
|
+
* Generated app updates:
|
4
|
+
* Uses dotenv for config management
|
5
|
+
* Breaks middleware into pluggable pieces
|
6
|
+
* Dynamically sets app name in config
|
7
|
+
* Creates a random session secret
|
8
|
+
* Updates Ring to 0.2.4
|
9
|
+
* Evaluates app template as erb
|
10
|
+
* Improves CLI, including new `version` command
|
11
|
+
* Moves everything into the Pakyow namespace
|
12
|
+
* Adds ASCII art to `server` command \o/
|
13
|
+
|
1
14
|
# 0.10.2 / 2015-11-15
|
2
15
|
|
3
16
|
* Updates Ring to latest in generator
|
data/LICENSE
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
[gem]: https://rubygems.org/gems/pakyow
|
2
2
|
[travis]: https://travis-ci.org/pakyow/pakyow
|
3
3
|
[gemnasium]: https://gemnasium.com/pakyow/pakyow
|
4
|
+
[inchpages]: http://inch-ci.org/github/pakyow/pakyow
|
5
|
+
[codeclimate]: https://codeclimate.com/github/pakyow/pakyow
|
4
6
|
|
5
7
|
# Pakyow Framework [![Gitter chat](https://badges.gitter.im/pakyow/chat.svg)](https://gitter.im/pakyow/chat)
|
6
8
|
|
@@ -46,6 +48,7 @@ implements this protocol on the backend for initial rendering and in
|
|
46
48
|
[![Gem Version](https://badge.fury.io/rb/pakyow.svg)][gem]
|
47
49
|
[![Build Status](https://travis-ci.org/pakyow/pakyow.svg?branch=master)][travis]
|
48
50
|
[![Dependency Status](https://gemnasium.com/pakyow/pakyow.svg)][gemnasium]
|
51
|
+
[![Inline docs](http://inch-ci.org/github/pakyow/pakyow.svg?branch=master&style=flat)][inchpages]
|
49
52
|
|
50
53
|
---
|
51
54
|
|
@@ -61,7 +64,7 @@ implements this protocol on the backend for initial rendering and in
|
|
61
64
|
|
62
65
|
3. Move to the new directory and start the server:
|
63
66
|
|
64
|
-
`cd webapp; pakyow server`
|
67
|
+
`cd webapp; bundle exec pakyow server`
|
65
68
|
|
66
69
|
4. You'll find your project running at [http://localhost:3000](http://localhost:3000)!
|
67
70
|
|
data/bin/pakyow
CHANGED
@@ -1,18 +1,4 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
case ARGV.first
|
6
|
-
when 'new'
|
7
|
-
ARGV.shift
|
8
|
-
require File.join(PAK_PATH, 'generators/pakyow/app/app_generator')
|
9
|
-
Pakyow::Generators::AppGenerator.start
|
10
|
-
when 'server', 's'
|
11
|
-
ARGV.shift
|
12
|
-
require File.join(PAK_PATH, 'commands/server')
|
13
|
-
when 'console', 'c'
|
14
|
-
ARGV.shift
|
15
|
-
require File.join(PAK_PATH, 'commands/console')
|
16
|
-
when '--help', '-h', nil
|
17
|
-
puts File.open(File.join(PAK_PATH, 'commands/USAGE')).read
|
18
|
-
end
|
3
|
+
require "pakyow/command_line_interface"
|
4
|
+
Pakyow::CommandLineInterface.start
|
@@ -1,20 +1,25 @@
|
|
1
|
+
require 'erb'
|
1
2
|
require 'fileutils'
|
3
|
+
require 'securerandom'
|
4
|
+
require 'pakyow/version.rb'
|
2
5
|
|
3
6
|
module Pakyow
|
4
7
|
module Generators
|
5
8
|
class AppGenerator
|
6
9
|
class << self
|
7
|
-
def start
|
8
|
-
|
9
|
-
|
10
|
-
puts File.open(File.join(PAK_PATH, 'commands/USAGE-NEW')).read
|
11
|
-
else
|
12
|
-
generator = self.new(ARGV.first)
|
13
|
-
generator.build
|
14
|
-
end
|
10
|
+
def start(destination)
|
11
|
+
generator = self.new(destination)
|
12
|
+
generator.build
|
15
13
|
end
|
16
14
|
end
|
17
15
|
|
16
|
+
FILENAME_TRANSLATIONS = {
|
17
|
+
'rspec' => '.rspec',
|
18
|
+
'gitignore' => '.gitignore',
|
19
|
+
'env' => '.env',
|
20
|
+
'env.example' => '.env.example'
|
21
|
+
}
|
22
|
+
|
18
23
|
def initialize(dest)
|
19
24
|
@src = "#{File.expand_path('../', __FILE__)}/templates/."
|
20
25
|
@dest = dest
|
@@ -38,23 +43,51 @@ module Pakyow
|
|
38
43
|
end
|
39
44
|
|
40
45
|
exec
|
41
|
-
puts "Done! Run `cd #{@dest}; pakyow server` to get started!"
|
46
|
+
puts "Done! Run `cd #{@dest}; bundle exec pakyow server` to get started!"
|
42
47
|
end
|
43
48
|
|
44
49
|
protected
|
45
50
|
|
46
|
-
# copies src files to dest
|
47
51
|
def copy
|
48
|
-
FileUtils.
|
52
|
+
FileUtils.mkdir(@dest) unless File.exists?(@dest)
|
53
|
+
|
54
|
+
Dir.glob(File.join(@src, '**', '*')).each do |path|
|
55
|
+
relative_path = path[@src.length..-1]
|
56
|
+
generated_path = File.join(@dest, File.dirname(relative_path), translated_filename(File.basename(relative_path)))
|
57
|
+
|
58
|
+
if File.directory?(path)
|
59
|
+
FileUtils.mkdir(generated_path)
|
60
|
+
next
|
61
|
+
end
|
62
|
+
|
63
|
+
erb = ERB.new(File.read(path))
|
64
|
+
File.open(generated_path, 'w') { |f| f.write(erb.result(binding)) }
|
65
|
+
end
|
49
66
|
end
|
50
67
|
|
51
|
-
# performs and other setup (e.g. bundle install)
|
52
68
|
def exec
|
53
69
|
FileUtils.cd(@dest) do
|
54
70
|
puts "Running `bundle install` in #{Dir.pwd}"
|
55
|
-
system("bundle install")
|
71
|
+
system("bundle install --binstubs")
|
56
72
|
end
|
57
73
|
end
|
74
|
+
|
75
|
+
def generating_locally?
|
76
|
+
local_pakyow = Gem::Specification.sort_by{ |g| [g.name.downcase, g.version] }.group_by{ |g| g.name }.detect{|k,v| k == 'pakyow'}
|
77
|
+
!local_pakyow || local_pakyow.last.last.version < Gem::Version.new(Pakyow::VERSION)
|
78
|
+
end
|
79
|
+
|
80
|
+
def translated_filename(filename)
|
81
|
+
FILENAME_TRANSLATIONS.fetch(filename, filename)
|
82
|
+
end
|
83
|
+
|
84
|
+
def generate_session_secret
|
85
|
+
SecureRandom.hex(64)
|
86
|
+
end
|
87
|
+
|
88
|
+
def app_name
|
89
|
+
File.basename(@dest)
|
90
|
+
end
|
58
91
|
end
|
59
92
|
end
|
60
93
|
end
|
@@ -1,15 +1,16 @@
|
|
1
1
|
source 'https://rubygems.org'
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
gem 'pakyow', '~> 0.10'
|
8
|
-
|
2
|
+
<% if generating_locally? %>
|
3
|
+
gem 'pakyow', github: 'pakyow/pakyow'
|
4
|
+
<% else %>
|
5
|
+
gem 'pakyow', '~> <%= Pakyow::VERSION %>'
|
6
|
+
<% end %>
|
9
7
|
# app server
|
10
8
|
gem 'puma', platforms: :ruby
|
11
9
|
gem 'thin', platforms: :mswin
|
12
10
|
|
11
|
+
# use dotenv to load environment variables
|
12
|
+
gem 'dotenv'
|
13
|
+
|
13
14
|
group :test do
|
14
15
|
gem 'rspec'
|
15
16
|
end
|
@@ -4,13 +4,13 @@ This is a Pakyow v0.10 project.
|
|
4
4
|
|
5
5
|
Start the server:
|
6
6
|
|
7
|
-
`pakyow server`
|
7
|
+
`bundle exec pakyow server`
|
8
8
|
|
9
9
|
You'll find your app running at [http://localhost:3000](http://localhost:3000)!
|
10
10
|
|
11
11
|
Need to interact with your code? Fire up a console:
|
12
12
|
|
13
|
-
`pakyow console`
|
13
|
+
`bundle exec pakyow console`
|
14
14
|
|
15
15
|
# Next Steps
|
16
16
|
|
@@ -2,32 +2,23 @@ require 'bundler/setup'
|
|
2
2
|
require 'pakyow'
|
3
3
|
|
4
4
|
Pakyow::App.define do
|
5
|
-
configure
|
5
|
+
configure do
|
6
6
|
Bundler.require :default, Pakyow::Config.env
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
# put development config here
|
14
|
-
end
|
8
|
+
if defined?(Dotenv)
|
9
|
+
env_path = ".env.#{Pakyow::Config.env}"
|
10
|
+
Dotenv.load env_path if File.exist?(env_path)
|
11
|
+
Dotenv.load
|
12
|
+
end
|
15
13
|
|
16
|
-
|
17
|
-
# an environment for running the front-end prototype with no backend
|
18
|
-
app.ignore_routes = true
|
14
|
+
app.name = '<%= app_name %>'
|
19
15
|
end
|
20
16
|
|
21
|
-
configure :
|
22
|
-
#
|
17
|
+
configure :development do
|
18
|
+
# development config goes here
|
23
19
|
end
|
24
20
|
|
25
21
|
configure :production do
|
26
|
-
#
|
27
|
-
end
|
28
|
-
|
29
|
-
middleware do |builder|
|
30
|
-
# TODO: you will most definitely want to change this secret
|
31
|
-
builder.use Rack::Session::Cookie, key: "#{Pakyow::Config.app.name}.session", secret: 'sekret'
|
22
|
+
# production config goes here
|
32
23
|
end
|
33
24
|
end
|
@@ -2,7 +2,7 @@
|
|
2
2
|
<html>
|
3
3
|
<head>
|
4
4
|
<title>
|
5
|
-
|
5
|
+
<%= app_name %>
|
6
6
|
</title>
|
7
7
|
|
8
8
|
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0" name="viewport">
|
@@ -13,6 +13,8 @@
|
|
13
13
|
<link rel="stylesheet" type="text/css" href="/styles/pakyow-css/theme.css">
|
14
14
|
|
15
15
|
<script src="/scripts/ring/pakyow.js"></script>
|
16
|
+
|
17
|
+
<meta charset="UTF-8">
|
16
18
|
</head>
|
17
19
|
|
18
20
|
<body>
|
@@ -0,0 +1 @@
|
|
1
|
+
SESSION_SECRET=<%= generate_session_secret %>
|
@@ -1,8 +1,9 @@
|
|
1
1
|
pw.component.register('fastlink', function (view, config) {
|
2
|
-
var that = this;
|
3
|
-
|
4
2
|
if (window.history) {
|
5
3
|
view.node.addEventListener('click', function (evt) {
|
4
|
+
// don't break open in new tab!
|
5
|
+
if (evt.metaKey || evt.ctrlKey) return;
|
6
|
+
|
6
7
|
evt.preventDefault();
|
7
8
|
window.history.pushState({ uri: this.href }, this.href, this.href);
|
8
9
|
return false;
|
@@ -1 +1 @@
|
|
1
|
-
pw.component.register("fastlink",function(t,e){window.history&&t.node.addEventListener("click",function(t){return t.preventDefault(),window.history.pushState({uri:this.href},this.href,this.href),!1})});
|
1
|
+
pw.component.register("fastlink",function(t,e){window.history&&t.node.addEventListener("click",function(t){return t.metaKey||t.ctrlKey?void 0:(t.preventDefault(),window.history.pushState({uri:this.href},this.href,this.href),!1)})});
|
@@ -0,0 +1,34 @@
|
|
1
|
+
/*
|
2
|
+
Ring.js - Google Analytics Component
|
3
|
+
|
4
|
+
Sets up Google Analytics and tracks the immediate pageview along with any
|
5
|
+
pageviews that occur over a WebSocket connection with Navigator. To use,
|
6
|
+
attach the component to the <body> tag and configure the trackingId:
|
7
|
+
|
8
|
+
<body data-ui="ga" data-config="trackingId: yourTrackingIdHere">
|
9
|
+
|
10
|
+
It will automatically ignore pageviews that occur locally.
|
11
|
+
*/
|
12
|
+
|
13
|
+
pw.component.register('ga', function (view, config) {
|
14
|
+
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
15
|
+
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
16
|
+
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
17
|
+
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
18
|
+
ga('create', config.trackingId, 'auto');
|
19
|
+
|
20
|
+
this.track = function (uri) {
|
21
|
+
if (document.domain.indexOf('local') != -1) {
|
22
|
+
return;
|
23
|
+
}
|
24
|
+
|
25
|
+
ga('send', 'pageview', uri);
|
26
|
+
};
|
27
|
+
|
28
|
+
this.listen('navigator:change', function (payload) {
|
29
|
+
this.track(payload.uri);
|
30
|
+
});
|
31
|
+
|
32
|
+
// track the current pageview
|
33
|
+
this.track(location.pathname);
|
34
|
+
});
|
@@ -0,0 +1 @@
|
|
1
|
+
pw.component.register("ga",function(t,n){!function(t,n,e,a,c,i,o){t.GoogleAnalyticsObject=c,t[c]=t[c]||function(){(t[c].q=t[c].q||[]).push(arguments)},t[c].l=1*new Date,i=n.createElement(e),o=n.getElementsByTagName(e)[0],i.async=1,i.src=a,o.parentNode.insertBefore(i,o)}(window,document,"script","//www.google-analytics.com/analytics.js","ga"),ga("create",n.trackingId,"auto"),this.track=function(t){-1==document.domain.indexOf("local")&&ga("send","pageview",t)},this.listen("navigator:change",function(t){this.track(t.uri)}),this.track(location.pathname)});
|
@@ -1 +1 @@
|
|
1
|
-
pw.component.register("modal",function(t,e,n,o){var a,i,d,l=this,r="modal:"+o;(d=document.querySelector('*[data-template="ui-modal-blinder"]'))&&(d=d.cloneNode(!0)),this.listen(r+":navigator:enter",function(t){a||(d?(a=d.cloneNode(!0),i=a.querySelector('*[data-template="ui-modal-content"]'),document.body.appendChild(a),a.removeAttribute("data-template"),i.removeAttribute("data-template")):(a=document.createElement("DIV"),a.classList.add("ui-modal-blinder"),i=document.createElement("DIV"),i.classList.add("ui-modal"),a.appendChild(i),document.body.appendChild(a)),a.addEventListener("click",function(t){if(t.target===a){t.preventDefault(),l.close();var e=window.location.pathname,n={uri:e};window.history.pushState(n,e,e)}})),i.innerHTML=t.body,pw.component.findAndInit(a),a.classList.add("ui-appear")}),this.listen(r+":navigator:exit",function(){l.close()}),this.listen(r+":navigator:boot",function(t){l.load(t)}),t.node.addEventListener("click",function(t){return t.preventDefault(),l.load(this.href),!1}),this.load=function(t){if(!window.socket)return void(document.location=t);var n={uri:t,context:"modal:"+o};e.container&&(n.container=e.container),e.partial&&(n.partial=e.partial),window.history.pushState(n,t,t)},this.close=function(){a&&i&&(pw.node.remove(a),a=null,i=null)}});
|
1
|
+
pw.component.register("modal",function(t,e,n,o){var a,i,d,l=this,r="modal:"+o;(d=document.querySelector('*[data-template="ui-modal-blinder"]'))&&(d=d.cloneNode(!0)),this.listen(r+":navigator:enter",function(t){a||(d?(a=d.cloneNode(!0),i=a.querySelector('*[data-template="ui-modal-content"]'),document.body.appendChild(a),a.removeAttribute("data-template"),i.removeAttribute("data-template")):(a=document.createElement("DIV"),a.classList.add("ui-modal-blinder"),i=document.createElement("DIV"),i.classList.add("ui-modal"),a.appendChild(i),document.body.appendChild(a)),a.addEventListener("click",function(t){if(t.target===a){t.preventDefault(),l.close();var e=window.location.pathname,n={uri:e};window.history.pushState(n,e,e)}})),i.innerHTML=t.body,pw.component.findAndInit(a),a.classList.add("ui-appear")}),this.listen(r+":navigator:exit",function(){l.close()}),this.listen(r+":navigator:boot",function(t){l.load(t)}),t.node.addEventListener("click",function(t){return t.preventDefault(),l.load(e.href||this.href),!1}),this.load=function(t){if(!window.socket)return void(document.location=t);var n={uri:t,context:"modal:"+o};e.container&&(n.container=e.container),e.partial&&(n.partial=e.partial),window.history.pushState(n,t,t)},this.close=function(){a&&i&&(pw.node.remove(a),a=null,i=null)}});
|
@@ -54,13 +54,17 @@ pw.component.register('mutable', function (view, config) {
|
|
54
54
|
}
|
55
55
|
} else if (res.status === 400) {
|
56
56
|
// bad request
|
57
|
+
pw.component.broadcast('response:received', { response: res });
|
57
58
|
return;
|
58
59
|
} else {
|
59
60
|
self.state.rollback();
|
60
61
|
}
|
61
62
|
|
62
63
|
pw.component.broadcast('response:received', { response: res });
|
63
|
-
|
64
|
+
|
65
|
+
if (config.revert !== 'false') {
|
66
|
+
self.revert();
|
67
|
+
}
|
64
68
|
});
|
65
69
|
}
|
66
70
|
});
|
@@ -1 +1 @@
|
|
1
|
-
pw.component.register("mutable",function(e,t){this.mutation=function(
|
1
|
+
pw.component.register("mutable",function(e,t){this.mutation=function(o){if(!window.socket)return void e.node.submit();var n=pw.util.dup(o);delete n.__nested,delete n.scope,delete n.id;var i={action:"call-route"};if("FORM"===e.node.tagName){if(e.node.querySelector('input[type="file"]'))return void e.node.submit();var r,s=e.node.querySelector('input[name="_method"]');r=s?s.value:e.node.getAttribute("method"),i.method=r,i.uri=e.node.getAttribute("action"),i.input=pw.node.serialize(e.node)}else{var a={};a[o.scope]=n,i.input=a}var d=this;window.socket.send(i,function(e){if(302===e.status){var o=e.headers.Location;o!=window.location.pathname||window.context&&"default"===window.context.name?history.pushState({uri:o},o,o):history.pushState({uri:o},o,o)}else{if(400===e.status)return void pw.component.broadcast("response:received",{response:e});d.state.rollback()}pw.component.broadcast("response:received",{response:e}),"false"!==t.revert&&d.revert()})}});
|
@@ -80,6 +80,9 @@ function handleState(state, direction) {
|
|
80
80
|
return;
|
81
81
|
}
|
82
82
|
|
83
|
+
uri = uri.replace(document.location.origin, '');
|
84
|
+
pw.component.broadcast('navigator:change', { uri: uri });
|
85
|
+
|
83
86
|
if (state.context) {
|
84
87
|
state.r_uri = document.location.pathname + '#:' + state.context + '/' + uri;
|
85
88
|
|
@@ -135,16 +138,17 @@ function handleState(state, direction) {
|
|
135
138
|
var body = payload.body[0];
|
136
139
|
|
137
140
|
if (body.match(/<title>/)) {
|
138
|
-
|
141
|
+
var title = body.split(/<title>/)[1].split('</title>')[0];
|
142
|
+
document.querySelector('title').innerHTML = title;
|
139
143
|
}
|
140
144
|
|
141
|
-
|
142
|
-
|
143
|
-
} else {
|
144
|
-
document.body.innerHTML = body;
|
145
|
-
}
|
145
|
+
var doc = document.documentElement.cloneNode();
|
146
|
+
doc.innerHTML = body;
|
146
147
|
|
148
|
+
document.body.innerHTML = doc.querySelector('body').innerHTML;
|
147
149
|
pw.component.findAndInit(document.querySelectorAll('body')[0]);
|
150
|
+
|
151
|
+
document.body.scrollTop = document.documentElement.scrollTop = 0;
|
148
152
|
}
|
149
153
|
});
|
150
154
|
}
|
@@ -1 +1 @@
|
|
1
|
-
function boot(){if(!window.socket)return void setTimeout(boot,100);if(window.location.hash){var t=window.location.hash.split("#:")[1].split("/"),n=t.shift(),o=t.join("/");pw.component.broadcast(n+":navigator:boot",o)}}function handleState(t,n){var o=t.uri||t.url;if(!window.socket)return void(document.location=o);if(t.context)t.r_uri=document.location.pathname+"#:"+t.context+"/"+o,window.context={_state:t,name:t.context,uri:t.r_uri,container:t.container,partial:t.partial};else if(t.r_uri=o,"default"!==window.context.name){if("back"===n)return pw.component.broadcast(window.context.name+":navigator:exit"),void(window.context={name:"default",uri:t.uri});t.r_uri=document.location.pathname+"#:"+window.context.name+"/"+o,t.context=window.context.name,t.container=window.context.container,t.partial=window.context.partial}var
|
1
|
+
function boot(){if(!window.socket)return void setTimeout(boot,100);if(window.location.hash){var t=window.location.hash.split("#:")[1].split("/"),n=t.shift(),o=t.join("/");pw.component.broadcast(n+":navigator:boot",o)}}function handleState(t,n){var o=t.uri||t.url;if(!window.socket)return void(document.location=o);if(o=o.replace(document.location.origin,""),pw.component.broadcast("navigator:change",{uri:o}),t.context)t.r_uri=document.location.pathname+"#:"+t.context+"/"+o,window.context={_state:t,name:t.context,uri:t.r_uri,container:t.container,partial:t.partial};else if(t.r_uri=o,"default"!==window.context.name){if("back"===n)return pw.component.broadcast(window.context.name+":navigator:exit"),void(window.context={name:"default",uri:t.uri});t.r_uri=document.location.pathname+"#:"+window.context.name+"/"+o,t.context=window.context.name,t.container=window.context.container,t.partial=window.context.partial}var e={uri:o,action:"call-route",method:"get"};t.container&&(e.container=t.container),t.partial&&(e.partial=t.partial),window.socket.send(e,function(n){if(t.context)pw.component.broadcast(t.context+":navigator:enter",n);else{var o=n.body[0];if(o.match(/<title>/)){var e=o.split(/<title>/)[1].split("</title>")[0];document.querySelector("title").innerHTML=e}var i=document.documentElement.cloneNode();i.innerHTML=o,document.body.innerHTML=i.querySelector("body").innerHTML,pw.component.findAndInit(document.querySelectorAll("body")[0]),document.body.scrollTop=document.documentElement.scrollTop=0}})}!function(t){if(pw.init.register(boot),t){var n=!1,o=t.pushState;t.pushState=function(e,i,a){return n=!0,"function"==typeof t.onpushstate&&t.onpushstate({state:e}),a==window.location.pathname?(pw.component.broadcast(window.context.name+":navigator:exit"),window.context={_state:e,name:"default",uri:window.location.href},e.r_uri=a):handleState(e,"forward"),o.apply(t,[e,i,e.r_uri])},window.onpopstate=function(t){if(n){var o=t.state;o||(o={}),o.uri||(o.uri=window.context.uri),handleState(o,"back")}}}}(window.history),window.context={name:"default",uri:window.location.href};
|
@@ -1,5 +1,5 @@
|
|
1
1
|
var pw = {
|
2
|
-
version: '0.
|
2
|
+
version: '0.2.4'
|
3
3
|
};
|
4
4
|
|
5
5
|
(function() {
|
@@ -142,7 +142,7 @@ pw.node = {
|
|
142
142
|
}
|
143
143
|
|
144
144
|
var next = node.parentNode;
|
145
|
-
if (next !== document) {
|
145
|
+
if (next && next !== document) {
|
146
146
|
return pw.node.inForm(next);
|
147
147
|
}
|
148
148
|
},
|
@@ -154,7 +154,7 @@ pw.node = {
|
|
154
154
|
}
|
155
155
|
|
156
156
|
var next = node.parentNode;
|
157
|
-
if (next !== document) {
|
157
|
+
if (next && next !== document) {
|
158
158
|
return pw.node.component(next);
|
159
159
|
}
|
160
160
|
},
|
@@ -166,7 +166,7 @@ pw.node = {
|
|
166
166
|
}
|
167
167
|
|
168
168
|
var next = node.parentNode;
|
169
|
-
if (next !== document) {
|
169
|
+
if (next && next !== document) {
|
170
170
|
return pw.node.scope(next);
|
171
171
|
}
|
172
172
|
},
|
@@ -178,7 +178,7 @@ pw.node = {
|
|
178
178
|
}
|
179
179
|
|
180
180
|
var next = node.parentNode;
|
181
|
-
if (next !== document) {
|
181
|
+
if (next && next !== document) {
|
182
182
|
return pw.node.scopeName(next);
|
183
183
|
}
|
184
184
|
},
|
@@ -190,7 +190,7 @@ pw.node = {
|
|
190
190
|
}
|
191
191
|
|
192
192
|
var next = node.parentNode;
|
193
|
-
if (next !== document) {
|
193
|
+
if (next && next !== document) {
|
194
194
|
return pw.node.prop(next);
|
195
195
|
}
|
196
196
|
},
|
@@ -202,7 +202,7 @@ pw.node = {
|
|
202
202
|
}
|
203
203
|
|
204
204
|
var next = node.parentNode;
|
205
|
-
if (next !== document) {
|
205
|
+
if (next && next !== document) {
|
206
206
|
return pw.node.propName(next);
|
207
207
|
}
|
208
208
|
},
|
@@ -215,11 +215,11 @@ pw.node = {
|
|
215
215
|
},
|
216
216
|
|
217
217
|
// creates a context in which view manipulations can occur
|
218
|
-
|
218
|
+
invoke: function(node, cb) {
|
219
219
|
cb.call(node);
|
220
220
|
},
|
221
221
|
|
222
|
-
|
222
|
+
invokeWithData: function(node, data, cb) {
|
223
223
|
if (pw.node.isNodeList(node)) {
|
224
224
|
node = pw.node.toA(node);
|
225
225
|
}
|
@@ -257,14 +257,14 @@ pw.node = {
|
|
257
257
|
},
|
258
258
|
|
259
259
|
repeat: function(node, data, cb) {
|
260
|
-
pw.node.
|
260
|
+
pw.node.invokeWithData(pw.node.match(node, data), data, cb);
|
261
261
|
},
|
262
262
|
|
263
263
|
// binds an object to a node
|
264
264
|
bind: function (data, node, cb) {
|
265
265
|
var scope = pw.node.findBindings(node)[0];
|
266
266
|
|
267
|
-
pw.node.
|
267
|
+
pw.node.invokeWithData(node, data, function(dm) {
|
268
268
|
if (!dm) {
|
269
269
|
return;
|
270
270
|
}
|
@@ -389,7 +389,9 @@ pw.node = {
|
|
389
389
|
} else if (node.tagName === 'TEXTAREA' || pw.node.isSelfClosingTag(node)) {
|
390
390
|
node.value = value;
|
391
391
|
} else {
|
392
|
-
|
392
|
+
if (value) {
|
393
|
+
node.innerHTML = value;
|
394
|
+
}
|
393
395
|
}
|
394
396
|
},
|
395
397
|
|
@@ -725,6 +727,12 @@ pw_State.prototype = {
|
|
725
727
|
|
726
728
|
// gets the current represented state from the node and diffs it with the current state
|
727
729
|
diffNode: function (node) {
|
730
|
+
if (node.hasAttribute('data-ui')) {
|
731
|
+
return {
|
732
|
+
'__nested': pw.state.build(pw.node.significant(node))
|
733
|
+
};
|
734
|
+
}
|
735
|
+
|
728
736
|
return pw.state.build(pw.node.significant(pw.node.scope(node)))[0];
|
729
737
|
},
|
730
738
|
|
@@ -758,7 +766,7 @@ pw_State.prototype = {
|
|
758
766
|
this.snapshots.push(copy);
|
759
767
|
},
|
760
768
|
|
761
|
-
|
769
|
+
remove: function (state) {
|
762
770
|
var copy = this.copy();
|
763
771
|
var match = copy.find(function (s) {
|
764
772
|
return s.id === state.id;
|
@@ -793,8 +801,15 @@ pw.view = {
|
|
793
801
|
},
|
794
802
|
|
795
803
|
fromStr: function (str) {
|
796
|
-
var
|
804
|
+
var nodeType = 'div';
|
805
|
+
|
806
|
+
if (str.match(/^<tr/) || str.match(/^<tbody/)) {
|
807
|
+
nodeType = 'table';
|
808
|
+
}
|
809
|
+
|
810
|
+
var e = document.createElement(nodeType);
|
797
811
|
e.innerHTML = str;
|
812
|
+
|
798
813
|
return pw.view.init(e.childNodes[0]);
|
799
814
|
}
|
800
815
|
};
|
@@ -839,16 +854,16 @@ pw_View.prototype = {
|
|
839
854
|
return pw.attrs.init(this);
|
840
855
|
},
|
841
856
|
|
842
|
-
|
843
|
-
pw.node.
|
857
|
+
invoke: function (cb) {
|
858
|
+
pw.node.invoke(this.node, cb);
|
844
859
|
},
|
845
860
|
|
846
861
|
match: function (data) {
|
847
862
|
pw.node.match(this.node, data);
|
848
863
|
},
|
849
864
|
|
850
|
-
|
851
|
-
pw.node.
|
865
|
+
invokeWithData: function (data, cb) {
|
866
|
+
pw.node.invokeWithData(this.node, data, cb);
|
852
867
|
},
|
853
868
|
|
854
869
|
repeat: function (data, cb) {
|
@@ -861,6 +876,41 @@ pw_View.prototype = {
|
|
861
876
|
|
862
877
|
apply: function (data, cb) {
|
863
878
|
pw.node.apply(data, this.node, cb);
|
879
|
+
},
|
880
|
+
|
881
|
+
use: function (version, cb) {
|
882
|
+
var self = this;
|
883
|
+
|
884
|
+
if (this.node.getAttribute('data-version') != version) {
|
885
|
+
this.node.setAttribute('data-version', version);
|
886
|
+
|
887
|
+
var lookup = {
|
888
|
+
scope: this.node.getAttribute('data-scope'),
|
889
|
+
version: version
|
890
|
+
};
|
891
|
+
|
892
|
+
window.socket.fetchView(lookup, function (view) {
|
893
|
+
view.node.setAttribute('data-channel', self.node.getAttribute('data-channel'));
|
894
|
+
pw.node.replace(self.node, view.node);
|
895
|
+
self.node = view.node;
|
896
|
+
cb();
|
897
|
+
});
|
898
|
+
} else {
|
899
|
+
cb();
|
900
|
+
}
|
901
|
+
},
|
902
|
+
|
903
|
+
setEndpoint: function (endpoint) {
|
904
|
+
this.endpoint = endpoint;
|
905
|
+
return this;
|
906
|
+
},
|
907
|
+
|
908
|
+
first: function () {
|
909
|
+
return this;
|
910
|
+
},
|
911
|
+
|
912
|
+
length: function () {
|
913
|
+
return 1;
|
864
914
|
}
|
865
915
|
};
|
866
916
|
|
@@ -875,7 +925,7 @@ pw_View.prototype = {
|
|
875
925
|
});
|
876
926
|
|
877
927
|
// pass through functions without view
|
878
|
-
['remove', 'clear', '
|
928
|
+
['remove', 'clear', 'versionName'].forEach(function (method) {
|
879
929
|
pw_View.prototype[method] = function () {
|
880
930
|
return pw.node[method](this.node);
|
881
931
|
};
|
@@ -908,7 +958,7 @@ pw.collection = {
|
|
908
958
|
var pw_Collection = function (views, parent, scope) {
|
909
959
|
this.views = views;
|
910
960
|
this.parent = parent;
|
911
|
-
this.
|
961
|
+
this._scope = scope;
|
912
962
|
};
|
913
963
|
|
914
964
|
pw_Collection.prototype = {
|
@@ -1007,11 +1057,11 @@ pw_Collection.prototype = {
|
|
1007
1057
|
return pw.collection.init(prependedViews);
|
1008
1058
|
},
|
1009
1059
|
|
1010
|
-
|
1011
|
-
pw.node.
|
1060
|
+
invoke: function (cb) {
|
1061
|
+
pw.node.invoke(this.views, cb);
|
1012
1062
|
},
|
1013
1063
|
|
1014
|
-
|
1064
|
+
invokeWithData: function(data, fn) {
|
1015
1065
|
data = Array.ensure(data);
|
1016
1066
|
|
1017
1067
|
this.views.forEach(function (view, i) {
|
@@ -1037,12 +1087,13 @@ pw_Collection.prototype = {
|
|
1037
1087
|
this.views.slice(0).forEach(function (view) {
|
1038
1088
|
var id = view.node.getAttribute('data-id');
|
1039
1089
|
|
1040
|
-
if (!id) {
|
1041
|
-
return;
|
1042
|
-
}
|
1043
|
-
|
1044
|
-
if (!data.find(function (datum) { return datum.id.toString() === id })) {
|
1090
|
+
if (!id && data[0].id) {
|
1045
1091
|
this.removeView(view);
|
1092
|
+
return;
|
1093
|
+
} else if (id) {
|
1094
|
+
if (!data.find(function (datum) { return datum.id && datum.id.toString() === id })) {
|
1095
|
+
this.removeView(view);
|
1096
|
+
}
|
1046
1097
|
}
|
1047
1098
|
}, this);
|
1048
1099
|
|
@@ -1083,12 +1134,12 @@ pw_Collection.prototype = {
|
|
1083
1134
|
|
1084
1135
|
repeat: function (data, fn) {
|
1085
1136
|
this.match(data, function () {
|
1086
|
-
this.
|
1137
|
+
this.invokeWithData(data, fn);
|
1087
1138
|
});
|
1088
1139
|
},
|
1089
1140
|
|
1090
1141
|
bind: function (data, fn) {
|
1091
|
-
this.
|
1142
|
+
this.invokeWithData(data, function(datum) {
|
1092
1143
|
this.bind(datum);
|
1093
1144
|
|
1094
1145
|
if(!(typeof fn === 'undefined')) {
|
@@ -1113,7 +1164,14 @@ pw_Collection.prototype = {
|
|
1113
1164
|
});
|
1114
1165
|
},
|
1115
1166
|
|
1116
|
-
|
1167
|
+
version: function (data, fn) {
|
1168
|
+
var self = this;
|
1169
|
+
this.match(data, function () {
|
1170
|
+
this.invokeWithData(data, fn);
|
1171
|
+
});
|
1172
|
+
},
|
1173
|
+
|
1174
|
+
setEndpoint: function (endpoint) {
|
1117
1175
|
this.endpoint = endpoint;
|
1118
1176
|
return this;
|
1119
1177
|
}
|
@@ -1320,11 +1378,10 @@ pw_Component.prototype = {
|
|
1320
1378
|
|
1321
1379
|
// make it mutable
|
1322
1380
|
var mutableCb = function (evt) {
|
1323
|
-
evt.preventDefault();
|
1324
|
-
|
1325
1381
|
var scope = pw.node.scope(evt.target);
|
1326
1382
|
|
1327
1383
|
if (scope) {
|
1384
|
+
evt.preventDefault();
|
1328
1385
|
self.mutated(scope);
|
1329
1386
|
}
|
1330
1387
|
};
|
@@ -1351,6 +1408,33 @@ pw_Component.prototype = {
|
|
1351
1408
|
pw.component.deregisterForBroadcast(channel, this);
|
1352
1409
|
},
|
1353
1410
|
|
1411
|
+
// Bubbles an event up to a parent component. Intended to be used
|
1412
|
+
// as an alternative to `broadcast` in cases where child components
|
1413
|
+
// have an impact on their parents.
|
1414
|
+
bubble: function (channel, payload) {
|
1415
|
+
var parentComponent = pw.node.component(this.node.parentNode);
|
1416
|
+
|
1417
|
+
(channelBroadcasts[channel] || []).forEach(function (cbTuple) {
|
1418
|
+
if (cbTuple[1].node == parentComponent) {
|
1419
|
+
cbTuple[0].call(cbTuple[1], payload);
|
1420
|
+
}
|
1421
|
+
});
|
1422
|
+
},
|
1423
|
+
|
1424
|
+
// Trickles an event down to child components. Intended to be used
|
1425
|
+
// as an alternative to `broadcast` in cases where parent components
|
1426
|
+
// have an impact on their children.
|
1427
|
+
trickle: function (channel, payload) {
|
1428
|
+
var channels = (channelBroadcasts[channel] || []);
|
1429
|
+
pw.node.toA(this.node.getElementsByTagName('*')).forEach(function (node) {
|
1430
|
+
channels.forEach(function (cbTuple) {
|
1431
|
+
if (cbTuple[1].node == node) {
|
1432
|
+
cbTuple[0].call(cbTuple[1], payload);
|
1433
|
+
}
|
1434
|
+
});
|
1435
|
+
})
|
1436
|
+
},
|
1437
|
+
|
1354
1438
|
//TODO this is pretty similary to processing instructions
|
1355
1439
|
// for views in that we also have to handle the empty case
|
1356
1440
|
//
|
@@ -1407,7 +1491,7 @@ pw_Component.prototype = {
|
|
1407
1491
|
}
|
1408
1492
|
|
1409
1493
|
if (state.length > 0) {
|
1410
|
-
this.view.scope(state[0].scope).
|
1494
|
+
this.view.scope(state[0].scope).setEndpoint(this.endpoint || this).apply(state);
|
1411
1495
|
} else {
|
1412
1496
|
pw.node.breadthFirst(this.view.node, function () {
|
1413
1497
|
if (this.hasAttribute('data-scope')) {
|
@@ -1435,8 +1519,8 @@ pw_Component.prototype = {
|
|
1435
1519
|
}
|
1436
1520
|
},
|
1437
1521
|
|
1438
|
-
|
1439
|
-
this.state.
|
1522
|
+
remove: function (data) {
|
1523
|
+
this.state.remove(data);
|
1440
1524
|
this.transform(this.state.current());
|
1441
1525
|
},
|
1442
1526
|
|
@@ -1470,6 +1554,7 @@ pw.init.register(function () {
|
|
1470
1554
|
pw.socket.init({
|
1471
1555
|
cb: function (socket) {
|
1472
1556
|
window.socket = socket;
|
1557
|
+
pw.component.broadcast('socket:available');
|
1473
1558
|
}
|
1474
1559
|
});
|
1475
1560
|
});
|
@@ -1671,7 +1756,9 @@ pw.instruct = {
|
|
1671
1756
|
});
|
1672
1757
|
},
|
1673
1758
|
|
1674
|
-
// TODO: make this smart and cache results
|
1759
|
+
// TODO: make this smart and cache results, invalidating
|
1760
|
+
// if the websocket connection reconnects (since that means
|
1761
|
+
// the server probably restarted)
|
1675
1762
|
template: function (view, cb) {
|
1676
1763
|
var lookup = {};
|
1677
1764
|
|
@@ -1696,23 +1783,46 @@ pw.instruct = {
|
|
1696
1783
|
});
|
1697
1784
|
},
|
1698
1785
|
|
1699
|
-
perform: function (collection, instructions) {
|
1786
|
+
perform: function (collection, instructions, cb) {
|
1700
1787
|
var self = this;
|
1788
|
+
instructions = instructions || [];
|
1701
1789
|
|
1702
|
-
|
1790
|
+
function instruct (subject, instruction) {
|
1703
1791
|
var method = instruction[0];
|
1704
1792
|
var value = instruction[1];
|
1705
1793
|
var nested = instruction[2];
|
1706
1794
|
|
1795
|
+
// remap instructions to the ring name
|
1796
|
+
if (method === 'with') {
|
1797
|
+
method = 'invoke';
|
1798
|
+
}
|
1799
|
+
|
1800
|
+
if (method === 'for') {
|
1801
|
+
method = 'invokeWithData';
|
1802
|
+
}
|
1803
|
+
|
1707
1804
|
if (collection[method]) {
|
1708
|
-
if (method == '
|
1709
|
-
|
1710
|
-
|
1805
|
+
if (method == 'invoke' || method == 'invokeWithData' || method == 'bind' || method == 'repeat' || method == 'apply' || method == 'version') {
|
1806
|
+
var cbLength = collection.length();
|
1807
|
+
var cbCount = 0;
|
1808
|
+
var nestedCb = function () {
|
1809
|
+
cbCount++;
|
1810
|
+
|
1811
|
+
if (cbCount == cbLength) {
|
1812
|
+
next();
|
1813
|
+
}
|
1814
|
+
}
|
1815
|
+
collection.setEndpoint(self)[method].call(collection, value, function (datum) {
|
1816
|
+
pw.instruct.perform(this, nested[value.indexOf(datum)], nestedCb);
|
1711
1817
|
});
|
1712
1818
|
return;
|
1713
1819
|
} else if (method == 'attrs') {
|
1714
1820
|
self.performAttr(collection.attrs(), nested);
|
1715
1821
|
return;
|
1822
|
+
} else if (method == 'use') {
|
1823
|
+
collection.setEndpoint(self);
|
1824
|
+
collection.use(value, next);
|
1825
|
+
return;
|
1716
1826
|
} else {
|
1717
1827
|
var mutatedViews = collection[method].call(collection, value);
|
1718
1828
|
}
|
@@ -1722,13 +1832,33 @@ pw.instruct = {
|
|
1722
1832
|
}
|
1723
1833
|
|
1724
1834
|
if (nested instanceof Array) {
|
1725
|
-
pw.instruct.perform(mutatedViews, nested);
|
1835
|
+
pw.instruct.perform(mutatedViews, nested, next);
|
1836
|
+
return;
|
1726
1837
|
} else if (mutatedViews) {
|
1727
1838
|
collection = mutatedViews;
|
1728
1839
|
}
|
1729
|
-
});
|
1730
1840
|
|
1731
|
-
|
1841
|
+
next();
|
1842
|
+
};
|
1843
|
+
|
1844
|
+
var i = 0;
|
1845
|
+
function next() {
|
1846
|
+
if (i < instructions.length) {
|
1847
|
+
instruct(collection, instructions[i++]);
|
1848
|
+
} else {
|
1849
|
+
done();
|
1850
|
+
}
|
1851
|
+
};
|
1852
|
+
|
1853
|
+
function done() {
|
1854
|
+
if (cb) {
|
1855
|
+
cb();
|
1856
|
+
} else {
|
1857
|
+
pw.component.findAndInit(collection.node);
|
1858
|
+
}
|
1859
|
+
};
|
1860
|
+
|
1861
|
+
next();
|
1732
1862
|
},
|
1733
1863
|
|
1734
1864
|
performAttr: function (context, attrInstructions) {
|