adhearsion 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +339 -0
- data/Rakefile +108 -0
- data/ahn +195 -0
- data/lib/adhearsion.rb +402 -0
- data/lib/constants.rb +20 -0
- data/lib/core_extensions.rb +157 -0
- data/lib/database_functions.rb +76 -0
- data/lib/rami.rb +822 -0
- data/lib/servlet_container.rb +146 -0
- data/new_projects/Rakefile +100 -0
- data/new_projects/config/adhearsion.sqlite3 +0 -0
- data/new_projects/config/adhearsion.yml +11 -0
- data/new_projects/config/database.rb +50 -0
- data/new_projects/config/database.yml +10 -0
- data/new_projects/config/helpers/drb_server.yml +43 -0
- data/new_projects/config/helpers/factorial.alien.c.yml +1 -0
- data/new_projects/config/helpers/manager_proxy.yml +7 -0
- data/new_projects/config/helpers/micromenus.yml +1 -0
- data/new_projects/config/helpers/micromenus/collab.rb +55 -0
- data/new_projects/config/helpers/micromenus/images/tux.bmp +0 -0
- data/new_projects/config/helpers/micromenus/javascripts/builder.js +131 -0
- data/new_projects/config/helpers/micromenus/javascripts/controls.js +834 -0
- data/new_projects/config/helpers/micromenus/javascripts/dragdrop.js +944 -0
- data/new_projects/config/helpers/micromenus/javascripts/effects.js +956 -0
- data/new_projects/config/helpers/micromenus/javascripts/prototype.js +2319 -0
- data/new_projects/config/helpers/micromenus/javascripts/scriptaculous.js +51 -0
- data/new_projects/config/helpers/micromenus/javascripts/slider.js +278 -0
- data/new_projects/config/helpers/micromenus/javascripts/unittest.js +557 -0
- data/new_projects/config/helpers/micromenus/stylesheets/firefox.css +10 -0
- data/new_projects/config/helpers/micromenus/stylesheets/firefox.xul.css +44 -0
- data/new_projects/config/helpers/weather.yml +1 -0
- data/new_projects/config/helpers/xbmc.yml +1 -0
- data/new_projects/config/migration.rb +53 -0
- data/new_projects/extensions.rb +56 -0
- data/new_projects/helpers/drb_server.rb +32 -0
- data/new_projects/helpers/factorial.alien.c +32 -0
- data/new_projects/helpers/manager_proxy.rb +43 -0
- data/new_projects/helpers/micromenus.rb +374 -0
- data/new_projects/helpers/oscar_wilde_quotes.rb +197 -0
- data/new_projects/helpers/weather.rb +85 -0
- data/new_projects/helpers/xbmc.rb +12 -0
- data/new_projects/logs/database.log +0 -0
- data/test/core_extensions_test.rb +26 -0
- data/test/dial_test.rb +43 -0
- data/test/stress_tests/test.rb +13 -0
- data/test/stress_tests/test.yml +13 -0
- data/test/test_micromenus.rb +0 -0
- metadata +131 -0
@@ -0,0 +1,146 @@
|
|
1
|
+
# Adhearsion, open source technology integrator
|
2
|
+
# Copyright 2006 Jay Phillips
|
3
|
+
#
|
4
|
+
# This program is free software; you can redistribute it and/or
|
5
|
+
# modify it under the terms of the GNU General Public License
|
6
|
+
# as published by the Free Software Foundation; either version 2
|
7
|
+
# of the License, or (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program; if not, write to the Free Software
|
16
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
17
|
+
|
18
|
+
class ServletContainer
|
19
|
+
|
20
|
+
# TODO: Port Mongrel server here.
|
21
|
+
class NativeServer;end
|
22
|
+
|
23
|
+
require 'gserver'
|
24
|
+
class RubyServer < GServer
|
25
|
+
def initialize(port, *args)
|
26
|
+
@stdlog = $STDOUT
|
27
|
+
log "Starting server on port #{port}"
|
28
|
+
audit = true
|
29
|
+
super(port, *args)
|
30
|
+
end
|
31
|
+
|
32
|
+
def read_variables io
|
33
|
+
# When debugging, this method could actually not do any IO operations and just
|
34
|
+
# return a development Hash of call variables
|
35
|
+
call_variables = {}
|
36
|
+
while(line = io.gets.chomp)
|
37
|
+
break if line.empty? # Empty lines signify no more variables
|
38
|
+
variable = line.split(/:\s*/)
|
39
|
+
new_name, new_value = variable.first[4..-1].downcase, variable.last
|
40
|
+
call_variables["#{new_name}"] = new_value =~ /^\d+$/ ? Integer(new_value) : new_value
|
41
|
+
end
|
42
|
+
call_variables
|
43
|
+
end
|
44
|
+
|
45
|
+
# GServer allows all functionality to be packed into
|
46
|
+
def serve io
|
47
|
+
io.sync = true
|
48
|
+
Thread.current[:io] = io
|
49
|
+
begin
|
50
|
+
call_variables = read_variables io
|
51
|
+
Thread.current[:VARS] = call_variables
|
52
|
+
Thread.current[:container] = Contexts::Container.new
|
53
|
+
|
54
|
+
if call_variables.extension == 'h'
|
55
|
+
# We've received a notification that a channel's hung up!
|
56
|
+
Thread.list.each do |thread|
|
57
|
+
if thread[:VARS] && thread[:VARS]['uniqueid'] == call_variables['uniqueid']
|
58
|
+
thread[:hungup?] = true
|
59
|
+
# Could kill() the Thread here but it may need to finish. We're merciful people.
|
60
|
+
log "Received notification that channel #{call_variables['channel']} has been hung up."
|
61
|
+
return
|
62
|
+
end
|
63
|
+
end
|
64
|
+
elsif ['t', 'failed'].include? call_variables['extension']
|
65
|
+
# Timeout notifications. Not implemented yet...
|
66
|
+
io.close
|
67
|
+
return
|
68
|
+
end
|
69
|
+
|
70
|
+
log "Executing call with variables: " + call_variables.inspect
|
71
|
+
|
72
|
+
# TODO Will perform cache checking here.
|
73
|
+
|
74
|
+
call_variables.each do |k,v|
|
75
|
+
Thread.current[:container].run_inside do
|
76
|
+
meta_def k do v end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Execute all before_call hooks
|
81
|
+
[$BEFORE_CALl_HIGH, $BEFORE_CALL, $BEFORE_CALL_LOW].flatten.compact.each &:call
|
82
|
+
+lambda { answer if CONFIG.answer_before_call }
|
83
|
+
|
84
|
+
# A call hook may decree that the call shouldn't be processed further (e.g. if
|
85
|
+
# it processed the call itself). This is done be rewriting the context variable.
|
86
|
+
unless Thread.current[:VARS]['context'] == :interrupted
|
87
|
+
|
88
|
+
begin
|
89
|
+
Contexts.new.instance_eval do
|
90
|
+
# Interpret the extensions.rb file!
|
91
|
+
eval File.read(File.join(Dir.pwd, "extensions.rb"))
|
92
|
+
end
|
93
|
+
rescue => detail
|
94
|
+
log "Exception raised in extensions.rb! " << detail.message
|
95
|
+
# TODO: Make error reports more intutive. Use notifications DSL?
|
96
|
+
detail.backtrace.each do |msg| log " "*8 << msg end
|
97
|
+
end
|
98
|
+
|
99
|
+
log "Parsing of extensions.rb complete"
|
100
|
+
begin
|
101
|
+
target_context = call_variables['context']
|
102
|
+
if target_context
|
103
|
+
Thread.current[:container].run_inside do
|
104
|
+
begin
|
105
|
+
+send(target_context.to_s.to_sym)
|
106
|
+
rescue => e
|
107
|
+
STDERR.puts e.inspect
|
108
|
+
+lambda {
|
109
|
+
play 'were-sorry'
|
110
|
+
hangup
|
111
|
+
}
|
112
|
+
end
|
113
|
+
end
|
114
|
+
else
|
115
|
+
io.close
|
116
|
+
log "ASTERISK REQUESTED ROUTING FOR A CONTEXT UNDEFINED IN extensions.rb!!! REQUEST IGNORED!!!"
|
117
|
+
return
|
118
|
+
end
|
119
|
+
rescue => detail
|
120
|
+
log "ERROR: #{detail.class.name} => #{detail.inspect}"
|
121
|
+
detail.backtrace.each do |msg| log " "*8 << msg end
|
122
|
+
end
|
123
|
+
log "Call routing complete"
|
124
|
+
end
|
125
|
+
rescue => detail
|
126
|
+
log "Call thread raised an exception! #{detail.message}"
|
127
|
+
detail.backtrace.each do |msg| log " "*8 << msg end
|
128
|
+
end
|
129
|
+
|
130
|
+
[$AFTER_CALl_HIGH, $AFTER_CALL, $AFTER_CALL_LOW].flatten.compact.each &:call
|
131
|
+
+lambda { hangup if CONFIG.hangup_after_call }
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def initialize(port=4573, native=false)
|
136
|
+
@server = (native ? NativeServer : RubyServer).new port, '0.0.0.0'
|
137
|
+
@server.start
|
138
|
+
end
|
139
|
+
|
140
|
+
|
141
|
+
def shutdown
|
142
|
+
@server.shutdown
|
143
|
+
end
|
144
|
+
|
145
|
+
attr_reader :server
|
146
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
require 'rake/gempackagetask'
|
5
|
+
|
6
|
+
ADHEARSION_VERSION = '0.7.0'
|
7
|
+
Summary = 'Adhearsion is a professional integration system for
|
8
|
+
integrating anything and everything.'
|
9
|
+
|
10
|
+
#task :default => [:test]
|
11
|
+
|
12
|
+
#desc "Run the Adhearsion unit tests"
|
13
|
+
#task :test do
|
14
|
+
# Dir['test/*.rb'].each do |f| require f end
|
15
|
+
#end
|
16
|
+
|
17
|
+
gem_spec = Gem::Specification.new do |s|
|
18
|
+
|
19
|
+
s.name = 'adhearsion'
|
20
|
+
s.rubyforge_project = 'adhearsion'
|
21
|
+
s.author = 'Jay Phillips'
|
22
|
+
s.email = 'admin -at- jicksta dot com'
|
23
|
+
s.version = ADHEARSION_VERSION
|
24
|
+
s.summary = Summary
|
25
|
+
s.homepage = 'http://adhearsion.com'
|
26
|
+
|
27
|
+
s.add_dependency 'activerecord', '>= 1.14.4'
|
28
|
+
s.add_dependency 'activesupport', '>= 1.3.1'
|
29
|
+
s.add_dependency 'rake', '>= 0.7.1'
|
30
|
+
|
31
|
+
s.platform = Gem::Platform::RUBY
|
32
|
+
s.require_path = 'lib'
|
33
|
+
s.executables = 'start_adhearsion' # ['start_adhearsion', 'ahn']
|
34
|
+
s.bindir = '.'
|
35
|
+
#s.extra_rdoc_files = ['LICENSE']
|
36
|
+
|
37
|
+
include = Dir['**/*']
|
38
|
+
exclude = Dir['{log,pkg}/**/*']
|
39
|
+
|
40
|
+
s.files = include - exclude
|
41
|
+
|
42
|
+
# test_files = Dir['tests/*.rb'] # Will be added when not so buggy
|
43
|
+
end
|
44
|
+
|
45
|
+
Rake::GemPackageTask.new gem_spec do |pkg|
|
46
|
+
pkg.need_zip = false
|
47
|
+
pkg.need_tar = false
|
48
|
+
end
|
49
|
+
|
50
|
+
#desc "Generate documentation for Adhearsion"
|
51
|
+
#Rake::RDocTask.new do |rdoc|
|
52
|
+
# rdoc.rdoc_dir 'doc'
|
53
|
+
#end
|
54
|
+
|
55
|
+
desc "Pulls down the entire wiki in HTML format"
|
56
|
+
task :wiki => [:rm_wiki] do
|
57
|
+
require 'open-uri'
|
58
|
+
File.open "wiki.zip",'a' do |f|
|
59
|
+
f.write open('http://adhearsion.stikipad.com/codex/export_html').read
|
60
|
+
end
|
61
|
+
Dir.mkdir 'docs' unless File.exists? 'docs'
|
62
|
+
`unzip -d docs/wiki wiki.zip`
|
63
|
+
File.delete 'wiki.zip'
|
64
|
+
puts `find docs/wiki`
|
65
|
+
end
|
66
|
+
|
67
|
+
desc "Removes all cached compiled RubyInline shared objects"
|
68
|
+
task :purge_objects do
|
69
|
+
`rm -rf ~/.ruby_inline/*`
|
70
|
+
end
|
71
|
+
|
72
|
+
desc "Removes the local copy of the wiki"
|
73
|
+
task :rm_wiki do
|
74
|
+
`rm -rf wiki.zip docs/wiki/`
|
75
|
+
end
|
76
|
+
|
77
|
+
desc "Prepares Adhearsion for a new release"
|
78
|
+
task :prepare_release do
|
79
|
+
# Remove log files
|
80
|
+
Dir['log/*.log'].each do |f|
|
81
|
+
puts "Removing file #{f}"
|
82
|
+
File.delete f
|
83
|
+
end
|
84
|
+
|
85
|
+
# Check for unversioned files
|
86
|
+
unversioned_files = `svn st | grep '^\?' | awk '{ print $2 }'`
|
87
|
+
puts "WARNING: These files are not under version control:\n#{unversioned_files}" unless unversioned_files.empty?
|
88
|
+
end
|
89
|
+
|
90
|
+
desc "Simply prints the Adhearsion version."
|
91
|
+
task :version do puts ADHEARSION_VERSION end
|
92
|
+
|
93
|
+
desc 'Create sample databases per the config/migration.rb and database.yml files.'
|
94
|
+
task :migrate do
|
95
|
+
require 'config/migration'
|
96
|
+
Module.constants.each do |c|
|
97
|
+
c = Module.const_get(c)
|
98
|
+
c.up if c.respond_to?(:superclass) && c.superclass == ActiveRecord::Migration
|
99
|
+
end
|
100
|
+
end
|
Binary file
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# Place any database customizations here. There are several ways you may desire enabling
|
2
|
+
# database connectivity:
|
3
|
+
#
|
4
|
+
# * Daemons (MySQL/PostgreSQL/etc)
|
5
|
+
# |
|
6
|
+
# | If you're running sophisticated applications for your VoIP server
|
7
|
+
# | and desire performance or integration, you'll want a daemon database
|
8
|
+
# | management system. MySQL is recommended.
|
9
|
+
# '____________________________________________________________
|
10
|
+
# * File-based (Sqlite/Sqlite3)
|
11
|
+
# |
|
12
|
+
# | If you have little desire to integrate your VoIP application's
|
13
|
+
# | user and group data with other apps, sqlite/3 is a very
|
14
|
+
# | easy solution to get running.
|
15
|
+
# '____________________________________________________________
|
16
|
+
# * No database
|
17
|
+
# |
|
18
|
+
# | If you simply have no use for keeping any information about
|
19
|
+
# | users, groups, or anything else, you can leave this entire
|
20
|
+
# | file commented. You don't even need ActiveRecord installed.
|
21
|
+
# '____________________________________________________________
|
22
|
+
#
|
23
|
+
### SETTING UP YOUR DATABASE WITH A SAMPLE SCHEMA
|
24
|
+
#
|
25
|
+
# If you would like run
|
26
|
+
#
|
27
|
+
# ActiveRecord resources:
|
28
|
+
# * http://slash7.com/cheats/activerecord_cheatsheet.pdf
|
29
|
+
#
|
30
|
+
#
|
31
|
+
# Uncomment the =begin/=end blocks to enable database access.
|
32
|
+
#=begin
|
33
|
+
|
34
|
+
require 'active_record'
|
35
|
+
ActiveRecord::Base.logger = Logger.new 'logs/database.log', 10, 1.megabyte
|
36
|
+
ActiveRecord::Base.establish_connection YAML.load_file('config/database.yml')
|
37
|
+
|
38
|
+
# When Adhearsion first encounters this Group class, it will automatically associate it
|
39
|
+
# to the "groups" database.
|
40
|
+
class Group < ActiveRecord::Base
|
41
|
+
has_many :user
|
42
|
+
end
|
43
|
+
|
44
|
+
class User < ActiveRecord::Base
|
45
|
+
validates_uniqueness_of :name
|
46
|
+
validates_presence_of :name
|
47
|
+
belongs_to :group
|
48
|
+
end
|
49
|
+
|
50
|
+
#=end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
adapter: sqlite3 # can also be "sqlite" for sqlite v2
|
2
|
+
dbfile: config/adhearsion.sqlite3
|
3
|
+
|
4
|
+
#### If you want to use MySQL, use these settings instead:
|
5
|
+
# adapter: mysql
|
6
|
+
# host: localhost
|
7
|
+
# database: adhearsion
|
8
|
+
# username: root
|
9
|
+
# password:
|
10
|
+
# # socket: /var/run/mysqld/mysqld.sock # May not be required
|
@@ -0,0 +1,43 @@
|
|
1
|
+
enabled: false
|
2
|
+
host: 127.0.0.1
|
3
|
+
port: 9050
|
4
|
+
|
5
|
+
deny: all
|
6
|
+
allow:
|
7
|
+
- 127.0.0.1
|
8
|
+
- 192.168.1.*
|
9
|
+
|
10
|
+
# Set your access control permissions above.
|
11
|
+
# Values for deny and allow can be several
|
12
|
+
# things:
|
13
|
+
#
|
14
|
+
# * The 'all' keyword can be used to match
|
15
|
+
# everything.
|
16
|
+
# * A single IP can be given right after
|
17
|
+
# the colon.
|
18
|
+
# * A single IP with wildcards can be given
|
19
|
+
# * Or, combining all of these, a YAML list
|
20
|
+
# can be used to specify many policies.
|
21
|
+
# See the comments below for more examples.
|
22
|
+
#
|
23
|
+
# Also note, the "host" field above may also
|
24
|
+
# affect the ability for DRb clients to connect
|
25
|
+
# to the server. If you wish you receive
|
26
|
+
# connections from the 192.168.1.*, listen on
|
27
|
+
# the IP associated with this machine on that
|
28
|
+
# subnet.
|
29
|
+
#
|
30
|
+
#
|
31
|
+
# USING YAML LISTS
|
32
|
+
#
|
33
|
+
# YAML allows lists of items to be created by
|
34
|
+
# prepending a hyphen to each list element.
|
35
|
+
# These lists can be used to refine your
|
36
|
+
# access control list better. Example:
|
37
|
+
#
|
38
|
+
# deny: all
|
39
|
+
# allow:
|
40
|
+
# - 192.168.1.123
|
41
|
+
# - 192.168.1.99
|
42
|
+
# - 66.199.34.44
|
43
|
+
|
@@ -0,0 +1 @@
|
|
1
|
+
enabled: false
|
@@ -0,0 +1 @@
|
|
1
|
+
port: 1337
|
@@ -0,0 +1,55 @@
|
|
1
|
+
heading "Adhearsion Micromenus Home"
|
2
|
+
|
3
|
+
# A simple example of a helper used in a Micromenu.
|
4
|
+
item oscar_wilde_quote + ' - Oscar Wilde'
|
5
|
+
|
6
|
+
# Use dial plan logic here in the Micromenu!
|
7
|
+
call "Check your voicemail!" do
|
8
|
+
check_voicemail
|
9
|
+
end
|
10
|
+
|
11
|
+
# If you have Adhearsion's Asterisk Manager Interface configured
|
12
|
+
# properly, you can use the guess_sip_user feature.
|
13
|
+
item "My User!" do
|
14
|
+
guess = guess_sip_user
|
15
|
+
item guess ? guess : "Sorry, you're behind a NAT or on a PC."
|
16
|
+
end
|
17
|
+
|
18
|
+
item "Employee Collaboration" do
|
19
|
+
item "View SIP users" do
|
20
|
+
sip_users = PBX.sip_users
|
21
|
+
items sip_users.collect(&:ip)
|
22
|
+
end
|
23
|
+
|
24
|
+
item "Add someone to the conference" do
|
25
|
+
# Originate a call into the user
|
26
|
+
end
|
27
|
+
item "Have Tweedledum call Tweedledee" do
|
28
|
+
x = PBX.rami_client.originate 'Context' => 'internal', 'Exten' => '11', 'Priority' => '1', 'Channel' => 'SIP/tweedledum'
|
29
|
+
item x.inspect
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
item 'Adhearsion Server Statistics' do
|
34
|
+
item 'View Registered SIP Users' do
|
35
|
+
PBX.sip_users.each do |u|
|
36
|
+
item %(SIP user "#{u.username}" on IP #{u.ip})
|
37
|
+
end
|
38
|
+
end
|
39
|
+
item 'View System Uptime' do
|
40
|
+
item `uptime`
|
41
|
+
end
|
42
|
+
item 'Network' do
|
43
|
+
heading 'Network Interface Info'
|
44
|
+
`ifconfig eth1`.each_line do |line|
|
45
|
+
item line
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
item 'View Users' do
|
51
|
+
item 'Select a user to call below.'
|
52
|
+
User.find(:all).each { |user| call user.ivr_extension, user.name }
|
53
|
+
end
|
54
|
+
|
55
|
+
image 'tux'
|
Binary file
|
@@ -0,0 +1,131 @@
|
|
1
|
+
// script.aculo.us builder.js v1.6.5, Wed Nov 08 14:17:49 CET 2006
|
2
|
+
|
3
|
+
// Copyright (c) 2005, 2006 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
|
4
|
+
//
|
5
|
+
// script.aculo.us is freely distributable under the terms of an MIT-style license.
|
6
|
+
// For details, see the script.aculo.us web site: http://script.aculo.us/
|
7
|
+
|
8
|
+
var Builder = {
|
9
|
+
NODEMAP: {
|
10
|
+
AREA: 'map',
|
11
|
+
CAPTION: 'table',
|
12
|
+
COL: 'table',
|
13
|
+
COLGROUP: 'table',
|
14
|
+
LEGEND: 'fieldset',
|
15
|
+
OPTGROUP: 'select',
|
16
|
+
OPTION: 'select',
|
17
|
+
PARAM: 'object',
|
18
|
+
TBODY: 'table',
|
19
|
+
TD: 'table',
|
20
|
+
TFOOT: 'table',
|
21
|
+
TH: 'table',
|
22
|
+
THEAD: 'table',
|
23
|
+
TR: 'table'
|
24
|
+
},
|
25
|
+
// note: For Firefox < 1.5, OPTION and OPTGROUP tags are currently broken,
|
26
|
+
// due to a Firefox bug
|
27
|
+
node: function(elementName) {
|
28
|
+
elementName = elementName.toUpperCase();
|
29
|
+
|
30
|
+
// try innerHTML approach
|
31
|
+
var parentTag = this.NODEMAP[elementName] || 'div';
|
32
|
+
var parentElement = document.createElement(parentTag);
|
33
|
+
try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
|
34
|
+
parentElement.innerHTML = "<" + elementName + "></" + elementName + ">";
|
35
|
+
} catch(e) {}
|
36
|
+
var element = parentElement.firstChild || null;
|
37
|
+
|
38
|
+
// see if browser added wrapping tags
|
39
|
+
if(element && (element.tagName != elementName))
|
40
|
+
element = element.getElementsByTagName(elementName)[0];
|
41
|
+
|
42
|
+
// fallback to createElement approach
|
43
|
+
if(!element) element = document.createElement(elementName);
|
44
|
+
|
45
|
+
// abort if nothing could be created
|
46
|
+
if(!element) return;
|
47
|
+
|
48
|
+
// attributes (or text)
|
49
|
+
if(arguments[1])
|
50
|
+
if(this._isStringOrNumber(arguments[1]) ||
|
51
|
+
(arguments[1] instanceof Array)) {
|
52
|
+
this._children(element, arguments[1]);
|
53
|
+
} else {
|
54
|
+
var attrs = this._attributes(arguments[1]);
|
55
|
+
if(attrs.length) {
|
56
|
+
try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
|
57
|
+
parentElement.innerHTML = "<" +elementName + " " +
|
58
|
+
attrs + "></" + elementName + ">";
|
59
|
+
} catch(e) {}
|
60
|
+
element = parentElement.firstChild || null;
|
61
|
+
// workaround firefox 1.0.X bug
|
62
|
+
if(!element) {
|
63
|
+
element = document.createElement(elementName);
|
64
|
+
for(attr in arguments[1])
|
65
|
+
element[attr == 'class' ? 'className' : attr] = arguments[1][attr];
|
66
|
+
}
|
67
|
+
if(element.tagName != elementName)
|
68
|
+
element = parentElement.getElementsByTagName(elementName)[0];
|
69
|
+
}
|
70
|
+
}
|
71
|
+
|
72
|
+
// text, or array of children
|
73
|
+
if(arguments[2])
|
74
|
+
this._children(element, arguments[2]);
|
75
|
+
|
76
|
+
return element;
|
77
|
+
},
|
78
|
+
_text: function(text) {
|
79
|
+
return document.createTextNode(text);
|
80
|
+
},
|
81
|
+
|
82
|
+
ATTR_MAP: {
|
83
|
+
'className': 'class',
|
84
|
+
'htmlFor': 'for'
|
85
|
+
},
|
86
|
+
|
87
|
+
_attributes: function(attributes) {
|
88
|
+
var attrs = [];
|
89
|
+
for(attribute in attributes)
|
90
|
+
attrs.push((attribute in this.ATTR_MAP ? this.ATTR_MAP[attribute] : attribute) +
|
91
|
+
'="' + attributes[attribute].toString().escapeHTML() + '"');
|
92
|
+
return attrs.join(" ");
|
93
|
+
},
|
94
|
+
_children: function(element, children) {
|
95
|
+
if(typeof children=='object') { // array can hold nodes and text
|
96
|
+
children.flatten().each( function(e) {
|
97
|
+
if(typeof e=='object')
|
98
|
+
element.appendChild(e)
|
99
|
+
else
|
100
|
+
if(Builder._isStringOrNumber(e))
|
101
|
+
element.appendChild(Builder._text(e));
|
102
|
+
});
|
103
|
+
} else
|
104
|
+
if(Builder._isStringOrNumber(children))
|
105
|
+
element.appendChild(Builder._text(children));
|
106
|
+
},
|
107
|
+
_isStringOrNumber: function(param) {
|
108
|
+
return(typeof param=='string' || typeof param=='number');
|
109
|
+
},
|
110
|
+
build: function(html) {
|
111
|
+
var element = this.node('div');
|
112
|
+
$(element).update(html.strip());
|
113
|
+
return element.down();
|
114
|
+
},
|
115
|
+
dump: function(scope) {
|
116
|
+
if(typeof scope != 'object' && typeof scope != 'function') scope = window; //global scope
|
117
|
+
|
118
|
+
var tags = ("A ABBR ACRONYM ADDRESS APPLET AREA B BASE BASEFONT BDO BIG BLOCKQUOTE BODY " +
|
119
|
+
"BR BUTTON CAPTION CENTER CITE CODE COL COLGROUP DD DEL DFN DIR DIV DL DT EM FIELDSET " +
|
120
|
+
"FONT FORM FRAME FRAMESET H1 H2 H3 H4 H5 H6 HEAD HR HTML I IFRAME IMG INPUT INS ISINDEX "+
|
121
|
+
"KBD LABEL LEGEND LI LINK MAP MENU META NOFRAMES NOSCRIPT OBJECT OL OPTGROUP OPTION P "+
|
122
|
+
"PARAM PRE Q S SAMP SCRIPT SELECT SMALL SPAN STRIKE STRONG STYLE SUB SUP TABLE TBODY TD "+
|
123
|
+
"TEXTAREA TFOOT TH THEAD TITLE TR TT U UL VAR").split(/\s+/);
|
124
|
+
|
125
|
+
tags.each( function(tag){
|
126
|
+
scope[tag] = function() {
|
127
|
+
return Builder.node.apply(Builder, [tag].concat($A(arguments)));
|
128
|
+
}
|
129
|
+
});
|
130
|
+
}
|
131
|
+
}
|