adhearsion 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
}
|