tiddlywiki_cp 0.0.1
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/COPYING +674 -0
- data/History.txt +4 -0
- data/Manifest.txt +68 -0
- data/README.txt +3 -0
- data/Rakefile +123 -0
- data/bin/tiddlywiki_cp +13 -0
- data/lib/tiddlywiki_cp/converters.rb +36 -0
- data/lib/tiddlywiki_cp/file2file.rb +32 -0
- data/lib/tiddlywiki_cp/file2tiddler.rb +42 -0
- data/lib/tiddlywiki_cp/r4tw.rb +763 -0
- data/lib/tiddlywiki_cp/tiddler2directory.rb +27 -0
- data/lib/tiddlywiki_cp/tiddler2file.rb +41 -0
- data/lib/tiddlywiki_cp/tiddler2tiddlywiki.rb +29 -0
- data/lib/tiddlywiki_cp/tiddler_css.rb +39 -0
- data/lib/tiddlywiki_cp/tiddler_html.rb +39 -0
- data/lib/tiddlywiki_cp/tiddler_js.rb +39 -0
- data/lib/tiddlywiki_cp/tiddlywiki2file.rb +29 -0
- data/lib/tiddlywiki_cp/version.rb +24 -0
- data/lib/tiddlywiki_cp.rb +378 -0
- data/scripts/txt2html +67 -0
- data/setup.rb +1585 -0
- data/test/content/a +0 -0
- data/test/content/a.div +0 -0
- data/test/content/b +0 -0
- data/test/content/e +3 -0
- data/test/content/e.div +1 -0
- data/test/content/html_entities.html +6 -0
- data/test/content/test_fetch.html +6 -0
- data/test/content/universe.html +10522 -0
- data/test/r4tw/addtag.rb +93 -0
- data/test/r4tw/all.rb +29 -0
- data/test/r4tw/createfrom.rb +62 -0
- data/test/r4tw/empties/2.1.3.html +7087 -0
- data/test/r4tw/empties/2.2.0.beta5.html +8726 -0
- data/test/r4tw/fromremote.rb +19 -0
- data/test/r4tw/fromurl.rb +28 -0
- data/test/r4tw/shadows.rb +27 -0
- data/test/r4tw/tiddler.rb +70 -0
- data/test/r4tw/tiddlerfromurl.rb +23 -0
- data/test/r4tw/tiddlywiki.rb +66 -0
- data/test/r4tw/utils.rb +55 -0
- data/test/r4tw/withcontent/2.2.0.beta5.html +8739 -0
- data/test/r4tw/withcontent/22b5index.html +13523 -0
- data/test/r4tw/withcontent/empty2.html +7084 -0
- data/test/r4tw/withcontent/nothing.js +1 -0
- data/test/r4tw/write_all_tiddlers_to.rb +62 -0
- data/test/test_all.rb +8 -0
- data/test/test_helper.rb +36 -0
- data/test/test_tiddler_css.rb +55 -0
- data/test/test_tiddler_html.rb +54 -0
- data/test/test_tiddler_js.rb +56 -0
- data/test/test_tiddlywiki_cp.rb +341 -0
- data/website/files/DefaultTiddlers.tiddler +2 -0
- data/website/files/DefaultTiddlers.tiddler.div +1 -0
- data/website/files/Introduction.tiddler +12 -0
- data/website/files/Introduction.tiddler.div +1 -0
- data/website/files/MainMenu.tiddler +1 -0
- data/website/files/MainMenu.tiddler.div +1 -0
- data/website/files/SiteSubtitle.tiddler +1 -0
- data/website/files/SiteSubtitle.tiddler.div +1 -0
- data/website/files/SiteTitle.tiddler +1 -0
- data/website/files/SiteTitle.tiddler.div +1 -0
- data/website/files/Usage.tiddler +55 -0
- data/website/files/Usage.tiddler.div +1 -0
- data/website/files/WebDAVSavingPlugin.js +234 -0
- data/website/files/WebDAVSavingPlugin.js.div +1 -0
- data/website/index.html +9144 -0
- data/website/index.xml +336 -0
- metadata +116 -0
@@ -0,0 +1,763 @@
|
|
1
|
+
#
|
2
|
+
# =r4tw
|
3
|
+
# Author:: Simon Baird
|
4
|
+
# URL:: http://simonbaird.com/r4tw
|
5
|
+
# License:: http://en.wikipedia.org/wiki/MIT_license
|
6
|
+
# r4tw is some ruby classes for manipuating TiddlyWikis and tiddlers.
|
7
|
+
# It is similar to cook and ginsu but cooler.
|
8
|
+
#
|
9
|
+
# <i>$Rev: 2238 $</i>
|
10
|
+
#
|
11
|
+
# ===Known problems
|
12
|
+
# from_remote_tw can be problematic if importing from a 2.1 TW into a 2.2 TW.
|
13
|
+
#
|
14
|
+
|
15
|
+
|
16
|
+
#---------------------------------------------------------------------
|
17
|
+
#-- General purpose utils
|
18
|
+
|
19
|
+
require 'pathname'
|
20
|
+
require 'open-uri'
|
21
|
+
|
22
|
+
def read_file(file_name) #:nodoc:
|
23
|
+
File.read(file_name)
|
24
|
+
end
|
25
|
+
|
26
|
+
def fetch_url(url) #:nodoc:
|
27
|
+
open(url).read.to_s
|
28
|
+
end
|
29
|
+
|
30
|
+
def this_dir(this_file=$0) #:nodoc:
|
31
|
+
Pathname.new(this_file).expand_path.dirname
|
32
|
+
end
|
33
|
+
|
34
|
+
class String
|
35
|
+
def to_file(file_name) #:nodoc:
|
36
|
+
File.open(file_name,"w") { |f| f << self }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
#---------------------------------------------------------------------
|
42
|
+
#-- TiddlyWiki related utils
|
43
|
+
|
44
|
+
class String
|
45
|
+
|
46
|
+
def escapeLineBreaks
|
47
|
+
gsub(/\\/m,"\\s").gsub(/\n/m,"\\n").gsub(/\r/m,"")
|
48
|
+
end
|
49
|
+
|
50
|
+
def unescapeLineBreaks
|
51
|
+
# not sure what \b is for
|
52
|
+
gsub(/\\n/m,"\n").gsub(/\\b/m," ").gsub(/\\s/,"\\").gsub(/\r/m,"")
|
53
|
+
end
|
54
|
+
|
55
|
+
def encodeHTML
|
56
|
+
gsub(/&/m,"&").gsub(/</m,"<").gsub(/>/m,">").gsub(/\"/m,""")
|
57
|
+
end
|
58
|
+
|
59
|
+
def decodeHTML
|
60
|
+
gsub(/&/m,"&").gsub(/</m,"<").gsub(/>/m,">").gsub(/"/m,"\"")
|
61
|
+
end
|
62
|
+
|
63
|
+
def readBrackettedList
|
64
|
+
# scan is a beautiful thing
|
65
|
+
scan(/\[\[([^\]]+)\]\]|(\S+)/).map {|m| m[0]||m[1]}
|
66
|
+
end
|
67
|
+
|
68
|
+
def toBrackettedList
|
69
|
+
self
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
class Array
|
75
|
+
|
76
|
+
def toBrackettedList
|
77
|
+
map{ |i| (i =~ /\s/) ? ("[["+i+"]]") : i }.join(" ")
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
class Time
|
83
|
+
|
84
|
+
def convertToLocalYYYYMMDDHHMM()
|
85
|
+
self.localtime.strftime("%Y%m%d%H%M")
|
86
|
+
end
|
87
|
+
|
88
|
+
def convertToYYYYMMDDHHMM()
|
89
|
+
self.utc.strftime("%Y%m%d%H%M")
|
90
|
+
end
|
91
|
+
|
92
|
+
def Time.convertFromYYYYMMDDHHMM(date_string)
|
93
|
+
m = date_string.match(/(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/)
|
94
|
+
Time.utc(m[1],m[2],m[3],m[4],m[5])
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
#---------------------------------------------------------------------
|
101
|
+
# Tiddler
|
102
|
+
#
|
103
|
+
|
104
|
+
# =Tiddler
|
105
|
+
# For creating and manipulating tiddlers
|
106
|
+
# ===Example
|
107
|
+
# puts Tiddler.new({'tiddler'=>'Hello','text'=>'Hi there','tags'=>['tag1','tag2']})
|
108
|
+
|
109
|
+
class Tiddler
|
110
|
+
|
111
|
+
@@main_fields = %w[tiddler modifier modified created tags]
|
112
|
+
# and soon to be changecount?
|
113
|
+
|
114
|
+
# text is not really a field in TiddlyWiki it makes
|
115
|
+
# things easier to make it one here. It could possibly
|
116
|
+
# clash with a real field called text. Ignore this fact for now...
|
117
|
+
|
118
|
+
@@defaults = {
|
119
|
+
'tiddler' => 'New Tiddler',
|
120
|
+
'modified' => Time.now.convertToYYYYMMDDHHMM,
|
121
|
+
'created' => Time.now.convertToYYYYMMDDHHMM,
|
122
|
+
'modifier' => 'YourName',
|
123
|
+
'tags' => '',
|
124
|
+
'text' => '',
|
125
|
+
}
|
126
|
+
|
127
|
+
# used by from_file
|
128
|
+
@@default_ext_tag_map = {
|
129
|
+
'.js' => %[systemConfig],
|
130
|
+
'.html' => %[html],
|
131
|
+
'.css' => %[css],
|
132
|
+
'.pub' => %[contentPublisher],
|
133
|
+
'.palette' => %[palette],
|
134
|
+
}
|
135
|
+
|
136
|
+
attr_accessor :fields
|
137
|
+
|
138
|
+
# Depending on the arguments this can be used to create or import a tiddler in various ways.
|
139
|
+
#
|
140
|
+
# ===From scratch
|
141
|
+
# If the argument is a Hash then it is used to specify a tiddler to be created from
|
142
|
+
# scratch.
|
143
|
+
#
|
144
|
+
# Example:
|
145
|
+
# t = Tiddler.new.from({
|
146
|
+
# 'tiddler'=>'HelloThere',
|
147
|
+
# 'text'=>'And welcome',
|
148
|
+
# })
|
149
|
+
# Other built-in fields are +modified+, +created+, +modifier+ and +tags+. Any other
|
150
|
+
# fields you add will be created as tiddler extended fields. Text is the contents of
|
151
|
+
# the tiddler. Tiddler is the title of the tiddler.
|
152
|
+
#
|
153
|
+
#
|
154
|
+
# ===From a file
|
155
|
+
# If the argument looks like a file name (ie a string that doesn't match the other
|
156
|
+
# criteria then create a tiddler with the name being the file name and the
|
157
|
+
# contents being the contents of the file. Does some guessing about tags based on
|
158
|
+
# the file's extension. (This is customisable, see code for details). Also reads the
|
159
|
+
# file modified date and uses it.
|
160
|
+
#
|
161
|
+
# Example:
|
162
|
+
# t = Tiddler.new.from("myplugin.js")
|
163
|
+
#
|
164
|
+
# ===From a TiddlyWiki
|
165
|
+
# If the argument is in the form file.html#TiddlerName or http://sitename.com/#TiddlerName
|
166
|
+
# then import TiddlerName from the specified location
|
167
|
+
#
|
168
|
+
# Example:
|
169
|
+
# t1 = Tiddler.new.from("myfile.html#SomeTiddler")
|
170
|
+
# t2 = Tiddler.new.from("http://www.tiddlywiki.com/#HelloThere")
|
171
|
+
#
|
172
|
+
#
|
173
|
+
# ===From a url
|
174
|
+
# Creates a tiddler from a url. The entire contents of the page are the contents
|
175
|
+
# of the tiddler. You should set the 'tiddler' field and other fields using a hash
|
176
|
+
# as the second argument in the same format as creating a tiddler from scratch.
|
177
|
+
# There is no automatic tagging for this one so you should add tags yourself as required
|
178
|
+
#
|
179
|
+
# Example:
|
180
|
+
# t = Tiddler.new.from(
|
181
|
+
# "http://svn.somewhere.org/Trunk/HelloWorld.js",
|
182
|
+
# {'tiddler'=>'HelloWorld','tags'=>'systemConfig'}
|
183
|
+
# )
|
184
|
+
#
|
185
|
+
#
|
186
|
+
# ===From a div string
|
187
|
+
# If the argument is a string containing a tiddler div such
|
188
|
+
# as would be found in a TiddlyWiki storeArea then the tiddler
|
189
|
+
# is created from that div
|
190
|
+
#
|
191
|
+
def initialize(*args)
|
192
|
+
@fields = {}
|
193
|
+
|
194
|
+
case args[0]
|
195
|
+
when Hash
|
196
|
+
from_scratch(*args)
|
197
|
+
|
198
|
+
when Tiddler
|
199
|
+
from_tiddler(*args)
|
200
|
+
|
201
|
+
when /^\s*<div/
|
202
|
+
from_div(*args)
|
203
|
+
|
204
|
+
when /#/
|
205
|
+
from_tiddler(from_tw(*args))
|
206
|
+
|
207
|
+
when /^(ftp|http|file):/
|
208
|
+
from_url(*args)
|
209
|
+
|
210
|
+
when String
|
211
|
+
from_file(*args)
|
212
|
+
|
213
|
+
end
|
214
|
+
|
215
|
+
end
|
216
|
+
|
217
|
+
|
218
|
+
# Intende to become private but not yet because
|
219
|
+
# all the test units use them
|
220
|
+
#
|
221
|
+
#private
|
222
|
+
|
223
|
+
def from_tiddler(other_tiddler)
|
224
|
+
@fields = {}
|
225
|
+
@fields.update(other_tiddler.fields)
|
226
|
+
end
|
227
|
+
|
228
|
+
def from_scratch(fields={}) #:nodoc:
|
229
|
+
@fields = @@defaults.merge(fields)
|
230
|
+
@fields['tags'] &&= @fields['tags'].toBrackettedList # in case it's an array
|
231
|
+
self
|
232
|
+
end
|
233
|
+
|
234
|
+
def from_div(div_str,use_pre=false) #:nodoc:
|
235
|
+
match_data = div_str.match(/<div([^>]+)>(.*?)<\/div>/m)
|
236
|
+
field_str = match_data[1]
|
237
|
+
text_str = match_data[2]
|
238
|
+
|
239
|
+
field_str.scan(/ ([\w\.]+)="([^"]+)"/) do |field_name,field_value|
|
240
|
+
if field_name == "title"
|
241
|
+
field_name = "tiddler"
|
242
|
+
end
|
243
|
+
@fields[field_name] = field_value
|
244
|
+
end
|
245
|
+
|
246
|
+
text_str.sub!(/\n<pre>/,'')
|
247
|
+
text_str.sub!(/<\/pre>\n/,'')
|
248
|
+
|
249
|
+
if (use_pre)
|
250
|
+
@fields['text'] = text_str.decodeHTML
|
251
|
+
else
|
252
|
+
@fields['text'] = text_str.unescapeLineBreaks.decodeHTML
|
253
|
+
end
|
254
|
+
|
255
|
+
self
|
256
|
+
end
|
257
|
+
|
258
|
+
def from_file(file_name, fields={}, ext_tag_map=@@default_ext_tag_map) #:nodoc:
|
259
|
+
ext = File.extname(file_name)
|
260
|
+
base = File.basename(file_name,ext)
|
261
|
+
@fields = @@defaults.merge(fields)
|
262
|
+
@fields['tiddler'] = base
|
263
|
+
@fields['text'] = read_file(file_name)
|
264
|
+
@fields['created'] = File.mtime(file_name).convertToYYYYMMDDHHMM
|
265
|
+
# @fields['modified'] = @fields['created']
|
266
|
+
@fields['tags'] = ext_tag_map[ext].toBrackettedList if ext_tag_map[ext]
|
267
|
+
self
|
268
|
+
end
|
269
|
+
|
270
|
+
def from_url(url,fields={}) #:nodoc:
|
271
|
+
@fields = @@defaults.merge(fields)
|
272
|
+
@fields['text'] = fetch_url(url)
|
273
|
+
self
|
274
|
+
end
|
275
|
+
|
276
|
+
def from_tw(tiddler_url) #:nodoc:
|
277
|
+
# this works if url is a local file, eg "somefile.html#TiddlerName"
|
278
|
+
# as well as if it's a remote file, eg "http://somewhere.com/#TiddlerName"
|
279
|
+
location,tiddler_name = tiddler_url.split("#")
|
280
|
+
TiddlyWiki.new.source_empty(location).get_tiddler(tiddler_name)
|
281
|
+
end
|
282
|
+
|
283
|
+
alias from_remote_tw from_tw #:nodoc:
|
284
|
+
alias from_local_tw from_tw #:nodoc:
|
285
|
+
|
286
|
+
# Returns a hash containing the tiddlers extended fields
|
287
|
+
# Probably would include changecount at this stage at least
|
288
|
+
def extended_fields
|
289
|
+
@fields.keys.reject{ |f| @@main_fields.include?(f) || f == 'text' }.sort
|
290
|
+
end
|
291
|
+
|
292
|
+
# Converts to a div suitable for a TiddlyWiki store area
|
293
|
+
def to_fields_string(use_pre=false)
|
294
|
+
|
295
|
+
@@main_fields.
|
296
|
+
reject{ |f|
|
297
|
+
use_pre and (
|
298
|
+
# seems like we have to leave out modified if there is none
|
299
|
+
(f == 'modified' and !@fields[f]) or
|
300
|
+
# seems like we have to not print tags="" any more
|
301
|
+
(f == 'tags' and (!@fields[f] or @fields[f].length == 0))
|
302
|
+
)
|
303
|
+
}.
|
304
|
+
map { |f|
|
305
|
+
# support old style tiddler=""
|
306
|
+
# and new style title=""
|
307
|
+
if f == 'tiddler' and use_pre
|
308
|
+
field_name = 'title'
|
309
|
+
else
|
310
|
+
field_name = f
|
311
|
+
end
|
312
|
+
%{#{field_name}="#{@fields[f]}"}
|
313
|
+
} +
|
314
|
+
extended_fields.
|
315
|
+
map{ |f| %{#{f}="#{@fields[f]}"} }
|
316
|
+
end
|
317
|
+
|
318
|
+
def to_s(use_pre=false)
|
319
|
+
fields_string = to_fields_string(use_pre)
|
320
|
+
if use_pre
|
321
|
+
"<div #{fields_string.join(' ')}>\n<pre>#{@fields['text'].encodeHTML}</pre>\n</div>"
|
322
|
+
else
|
323
|
+
"<div #{fields_string.join(' ')}>#{@fields['text'].escapeLineBreaks.encodeHTML}</div>"
|
324
|
+
end
|
325
|
+
|
326
|
+
end
|
327
|
+
|
328
|
+
alias to_div to_s #:nodoc:
|
329
|
+
|
330
|
+
# Lets you access fields like this:
|
331
|
+
# tiddler.name
|
332
|
+
# tiddler.created
|
333
|
+
# etc
|
334
|
+
#
|
335
|
+
def method_missing(method,*args)
|
336
|
+
|
337
|
+
method = method.to_s
|
338
|
+
|
339
|
+
synonyms = {
|
340
|
+
'name' => 'tiddler',
|
341
|
+
'title' => 'tiddler',
|
342
|
+
'content' => 'text',
|
343
|
+
'body' => 'text',
|
344
|
+
}
|
345
|
+
|
346
|
+
method = synonyms[method] || method
|
347
|
+
|
348
|
+
if @@main_fields.include? method or @fields[method]
|
349
|
+
@fields[method]
|
350
|
+
else
|
351
|
+
raise "No such field or method #{method}"
|
352
|
+
end
|
353
|
+
|
354
|
+
end
|
355
|
+
|
356
|
+
# Add some text to the end of a tiddler's content
|
357
|
+
def append_content(new_content)
|
358
|
+
@fields['text'] += new_content
|
359
|
+
self
|
360
|
+
end
|
361
|
+
|
362
|
+
# Add some text to the beginning of a tiddler's content
|
363
|
+
def prepend_content(new_content)
|
364
|
+
@fields['text'] = new_content + @fields['text']
|
365
|
+
self
|
366
|
+
end
|
367
|
+
|
368
|
+
# Renames a tiddler
|
369
|
+
def rename(new_name)
|
370
|
+
@fields['tiddler'] = new_name
|
371
|
+
self
|
372
|
+
end
|
373
|
+
|
374
|
+
# Makes a copy of this tiddler
|
375
|
+
def copy
|
376
|
+
Tiddler.new.from_div(self.to_div)
|
377
|
+
end
|
378
|
+
|
379
|
+
# Makes a copy of this tiddler with a new title
|
380
|
+
def copy_to(new_title)
|
381
|
+
copy.rename(new_title)
|
382
|
+
end
|
383
|
+
|
384
|
+
# Adds a tag
|
385
|
+
def add_tag(new_tag)
|
386
|
+
@fields['tags'] = @fields['tags'].
|
387
|
+
readBrackettedList.
|
388
|
+
push(new_tag).
|
389
|
+
uniq.
|
390
|
+
toBrackettedList
|
391
|
+
|
392
|
+
self
|
393
|
+
end
|
394
|
+
|
395
|
+
# Adds a list of tags
|
396
|
+
def add_tags(tags)
|
397
|
+
tags.each { |tag| add_tag(tag) }
|
398
|
+
end
|
399
|
+
|
400
|
+
# Removes a single tag
|
401
|
+
def remove_tag(old_tag)
|
402
|
+
@fields['tags'] = @fields['tags'].
|
403
|
+
readBrackettedList.
|
404
|
+
reject { |tag| tag == old_tag }.
|
405
|
+
toBrackettedList
|
406
|
+
|
407
|
+
self
|
408
|
+
end
|
409
|
+
|
410
|
+
# Removes a list of tags
|
411
|
+
def remove_tags(tags)
|
412
|
+
tags.each { |tag| remove_tags(tag) }
|
413
|
+
end
|
414
|
+
|
415
|
+
# Returns true if a tiddler has a particular tag
|
416
|
+
def has_tag(tag)
|
417
|
+
fields['tags'] && fields['tags'].readBrackettedList.include?(tag)
|
418
|
+
end
|
419
|
+
|
420
|
+
# Returns a Hash containing all tiddler slices
|
421
|
+
def get_slices
|
422
|
+
if not @slices
|
423
|
+
@slices = {}
|
424
|
+
# look familiar?
|
425
|
+
slice_re = /(?:[\'\/]*~?(\w+)[\'\/]*\:[\'\/]*\s*(.*?)\s*$)|(?:\|[\'\/]*~?(\w+)\:?[\'\/]*\|\s*(.*?)\s*\|)/m
|
426
|
+
text.scan(slice_re).each do |l1,v1,l2,v2|
|
427
|
+
@slices[l1||l2] = v1||v2;
|
428
|
+
end
|
429
|
+
end
|
430
|
+
@slices
|
431
|
+
end
|
432
|
+
|
433
|
+
# Returns a tiddler slice
|
434
|
+
def get_slice(slice)
|
435
|
+
get_slices[slice]
|
436
|
+
end
|
437
|
+
|
438
|
+
#
|
439
|
+
# Experimental. Provides access to plugin meta slices.
|
440
|
+
# Returns one meta value or a hash of them if no argument is given
|
441
|
+
#
|
442
|
+
def plugin_meta(slice=nil)
|
443
|
+
# see http://www.tiddlywiki.com/#ExamplePlugin
|
444
|
+
if not @plugin_meta
|
445
|
+
meta = %w[Name Description Version Date Source Author License CoreVersion Browser]
|
446
|
+
@plugin_meta = get_slices.reject{|k,v| not meta.include?(k)}
|
447
|
+
end
|
448
|
+
if slice
|
449
|
+
@plugin_meta[slice]
|
450
|
+
else
|
451
|
+
@plugin_meta
|
452
|
+
end
|
453
|
+
end
|
454
|
+
|
455
|
+
end
|
456
|
+
|
457
|
+
#---------------------------------------------------------------------
|
458
|
+
# =Tiddlywiki
|
459
|
+
# Create and manipulate TiddlyWiki files
|
460
|
+
#
|
461
|
+
|
462
|
+
class TiddlyWiki
|
463
|
+
|
464
|
+
attr_accessor :orig_tiddlers, :tiddlers, :raw
|
465
|
+
|
466
|
+
# doesn't do much. probably should allow an empty file param
|
467
|
+
def initialize(use_pre=false)
|
468
|
+
@use_pre = use_pre
|
469
|
+
@tiddlers = []
|
470
|
+
end
|
471
|
+
|
472
|
+
# this should replace all the add_tiddler_from_blah methods
|
473
|
+
# but actually they are still there below
|
474
|
+
# testing required
|
475
|
+
def method_missing(method_name,*args);
|
476
|
+
case method_name.to_s
|
477
|
+
when /^add_tiddler_(.*)$/
|
478
|
+
add_tiddler(Tiddler.new.send($1,*args))
|
479
|
+
end
|
480
|
+
end
|
481
|
+
|
482
|
+
# initialise a TiddlyWiki from a source file
|
483
|
+
# will treat empty_file as a url if it looks like one
|
484
|
+
# note that it doesn't have to be literally empty
|
485
|
+
def source_empty(empty_file,&block)
|
486
|
+
@empty_file = empty_file
|
487
|
+
if empty_file =~ /^https?/
|
488
|
+
@raw = fetch_url(@empty_file)
|
489
|
+
else
|
490
|
+
@raw = read_file(@empty_file)
|
491
|
+
end
|
492
|
+
|
493
|
+
# stupid ctrl (\r) char
|
494
|
+
#@raw.eat_ctrl_m!
|
495
|
+
|
496
|
+
if @raw =~ /var version = \{title: "TiddlyWiki", major: 2, minor: 2/
|
497
|
+
@use_pre = true
|
498
|
+
end
|
499
|
+
|
500
|
+
@core_hacks = []
|
501
|
+
@orig_tiddlers = get_orig_tiddlers
|
502
|
+
@tiddlers = @orig_tiddlers
|
503
|
+
|
504
|
+
instance_eval(&block) if block
|
505
|
+
|
506
|
+
self
|
507
|
+
end
|
508
|
+
|
509
|
+
# reads an empty from a file on disk
|
510
|
+
def source_file(file_name="empty.html")
|
511
|
+
source_empty(file_name)
|
512
|
+
end
|
513
|
+
|
514
|
+
# reads an empty file from a url
|
515
|
+
def source_url(url="http://www.tiddlywiki.com/empty.html")
|
516
|
+
source_empty(url)
|
517
|
+
end
|
518
|
+
|
519
|
+
# important regexp
|
520
|
+
# if this doesn't work we are screwed
|
521
|
+
@@store_regexp = /^(.*<div id="storeArea">\n?)(.*)(\n?<\/div>\r?\n<!--.*)$/m # stupid ctrl-m \r char
|
522
|
+
|
523
|
+
# everything before the store
|
524
|
+
# TODO make these private
|
525
|
+
def pre_store #:nodoc:
|
526
|
+
@raw.sub(@@store_regexp,'\1')
|
527
|
+
end
|
528
|
+
|
529
|
+
# the store itself
|
530
|
+
def store #:nodoc:
|
531
|
+
@raw.sub(@@store_regexp,'\2')
|
532
|
+
end
|
533
|
+
|
534
|
+
# everything after the store
|
535
|
+
def post_store #:nodoc:
|
536
|
+
@raw.sub(@@store_regexp,'\3')
|
537
|
+
end
|
538
|
+
|
539
|
+
# returns an array of tiddler divs
|
540
|
+
def tiddler_divs #:nodoc:
|
541
|
+
## the old way, one tiddler per line...
|
542
|
+
# store.strip.to_a
|
543
|
+
## the new way
|
544
|
+
store.scan(/(<div ti[^>]+>.*?<\/div>)/m).map { |m| m[0] }
|
545
|
+
# did I mention that scan is a beautiful thing?
|
546
|
+
end
|
547
|
+
|
548
|
+
# add a core hack
|
549
|
+
# it will be applied to the entire TW core like this gsub(regexp,replace)
|
550
|
+
def add_core_hack(regexp,replace)
|
551
|
+
# this is always a bad idea... ;)
|
552
|
+
@core_hacks.push([regexp,replace])
|
553
|
+
end
|
554
|
+
|
555
|
+
def get_orig_tiddlers #:nodoc:
|
556
|
+
tiddler_divs.map do |tiddler_div|
|
557
|
+
Tiddler.new.from_div(tiddler_div,@use_pre)
|
558
|
+
end
|
559
|
+
end
|
560
|
+
|
561
|
+
# an array of tiddler titles
|
562
|
+
def tiddler_titles
|
563
|
+
@tiddlers.map { |t| t.name }
|
564
|
+
end
|
565
|
+
|
566
|
+
# returns an array of tiddlers containing a particular tag
|
567
|
+
def tiddlers_with_tag(tag)
|
568
|
+
@tiddlers.select{|t| t.has_tag(tag)}
|
569
|
+
end
|
570
|
+
|
571
|
+
# adds a tiddler
|
572
|
+
def add_tiddler(tiddler)
|
573
|
+
remove_tiddler(tiddler.name)
|
574
|
+
@tiddlers << tiddler
|
575
|
+
tiddler
|
576
|
+
end
|
577
|
+
|
578
|
+
|
579
|
+
# removes a tiddler by name
|
580
|
+
def remove_tiddler(tiddler_name)
|
581
|
+
@tiddlers.reject!{|t| t.name == tiddler_name}
|
582
|
+
end
|
583
|
+
|
584
|
+
# adds a shadow tiddler
|
585
|
+
# note that tags and other fields aren't preserved
|
586
|
+
def add_shadow_tiddler(tiddler)
|
587
|
+
# shadow tiddlers currently implemented as core_hacks
|
588
|
+
add_core_hack(
|
589
|
+
/^\/\/ End of scripts\n/m,
|
590
|
+
"\\0\nconfig.shadowTiddlers[\"#{tiddler.name}\"] = #{tiddler.text.dump};\n\n"
|
591
|
+
)
|
592
|
+
end
|
593
|
+
|
594
|
+
# adds a shadow tiddler from a file
|
595
|
+
def add_shadow_tiddler_from_file(file_name)
|
596
|
+
add_shadow_tiddler Tiddler.new.from_file("#{file_name}")
|
597
|
+
end
|
598
|
+
|
599
|
+
# add tiddlers from a list of file names
|
600
|
+
# ignores file names starting with #
|
601
|
+
# so you can do this
|
602
|
+
# %w[
|
603
|
+
# foo
|
604
|
+
# bar
|
605
|
+
# #baz
|
606
|
+
# ]
|
607
|
+
# and it will skip baz
|
608
|
+
def add_tiddlers(file_names)
|
609
|
+
file_names.reject{|f| f.match(/^#/)}.each do |f|
|
610
|
+
add_tiddler_from_file(f)
|
611
|
+
end
|
612
|
+
end
|
613
|
+
|
614
|
+
def add_tiddler_from(*args)
|
615
|
+
add_tiddler Tiddler.new(*args)
|
616
|
+
end
|
617
|
+
|
618
|
+
def add_tiddlers_from(tiddler_list)
|
619
|
+
tiddler_list.each { |t| add_tiddler Tiddler.new(t) }
|
620
|
+
end
|
621
|
+
|
622
|
+
|
623
|
+
# add tiddlers from files found in directory dir_name
|
624
|
+
# TODO exclude pattern?
|
625
|
+
def add_tiddlers_from_dir(dir_name)
|
626
|
+
add_tiddlers(Dir.glob("#{dir_name}/*"))
|
627
|
+
end
|
628
|
+
|
629
|
+
# add shadow tiddlers from files found in directory dir_name
|
630
|
+
def add_shadow_tiddlers_from_dir(dir_name)
|
631
|
+
Dir.glob("#{dir_name}/*").each do |f|
|
632
|
+
add_shadow_tiddler_from_file(f)
|
633
|
+
end
|
634
|
+
end
|
635
|
+
|
636
|
+
# add a list of files found in dir_name packaged as javascript to create shadow tiddlers
|
637
|
+
# append the javascript to the contents of file_name
|
638
|
+
# (needs more explanation perhaps)
|
639
|
+
# see also package_as
|
640
|
+
def package_as_from_dir(file_name,dir_name)
|
641
|
+
package_as(file_name,Dir.glob("#{dir_name}/*"))
|
642
|
+
end
|
643
|
+
|
644
|
+
# if you have a file containing just tiddler divs you can read them
|
645
|
+
# all in with this
|
646
|
+
def add_tiddlers_from_file(file_name)
|
647
|
+
# a file full of divs
|
648
|
+
File.read(file_name).to_a.inject([]) do |tiddlers,tiddler_div|
|
649
|
+
@tiddlers << Tiddler.new.from_div(tiddler_div,@use_pre)
|
650
|
+
end
|
651
|
+
end
|
652
|
+
|
653
|
+
# get a tidler by name
|
654
|
+
def get_tiddler(tiddler_title)
|
655
|
+
@tiddlers.select{|t| t.name == tiddler_title}.first
|
656
|
+
end
|
657
|
+
|
658
|
+
# output the TiddlyWiki file
|
659
|
+
def to_s
|
660
|
+
pre_store_hacked = pre_store
|
661
|
+
post_store_hacked = post_store
|
662
|
+
@core_hacks.each do |hack|
|
663
|
+
pre_store_hacked.gsub!(hack[0],hack[1])
|
664
|
+
post_store_hacked.gsub!(hack[0],hack[1])
|
665
|
+
end
|
666
|
+
"#{pre_store_hacked}#{store_to_s}#{post_store_hacked}"
|
667
|
+
end
|
668
|
+
|
669
|
+
# output just the contents of the store
|
670
|
+
def store_to_s
|
671
|
+
# not sure about this bit. breaks some tests if I put it in
|
672
|
+
#((@use_pre and @tiddlers.length > 0) ? "\n" : "") +
|
673
|
+
@tiddlers.sort_by{|t| t.name}.inject(""){ |out,t|out << t.to_div(@use_pre) << "\n"}
|
674
|
+
end
|
675
|
+
|
676
|
+
# writes just the store area to a file
|
677
|
+
# the file can be used with ImportTiddlers to save download bandwidth
|
678
|
+
def store_to_file(file_name)
|
679
|
+
File.open(file_name,"w") { |f| f << "<div id=\"storeArea\">\n#{store_to_s}</div>" }
|
680
|
+
puts "Wrote store only to '#{file_name}'"
|
681
|
+
end
|
682
|
+
|
683
|
+
# writes just the store contents to a file
|
684
|
+
def store_to_divs(file_name)
|
685
|
+
File.open(file_name,"w") { |f| f << store_to_s }
|
686
|
+
puts "Wrote tiddlers only to '#{file_name}'"
|
687
|
+
end
|
688
|
+
|
689
|
+
# writes the entire TiddlyWiki to a file
|
690
|
+
def to_file(file_name)
|
691
|
+
File.open(file_name,"w") { |f| f << to_s }
|
692
|
+
# puts "Wrote tw file to '#{file_name}'"
|
693
|
+
end
|
694
|
+
|
695
|
+
# takes a list of file_names, reads their content
|
696
|
+
# and converts them to javascript creation of shadow tiddlers
|
697
|
+
# then appends that to the contents of file_name
|
698
|
+
# (sorry, confusing)
|
699
|
+
def package_as(file_name,package_file_names)
|
700
|
+
new_tiddler = add_tiddler Tiddler.new.from_file(file_name)
|
701
|
+
new_tiddler.append_content(package(package_file_names))
|
702
|
+
# date of the most recently modified
|
703
|
+
new_tiddler.fields['modified'] = package_file_names.push(file_name).map{|f| File.mtime(f)}.max.convertToYYYYMMDDHHMM
|
704
|
+
end
|
705
|
+
|
706
|
+
# TODO make private?
|
707
|
+
def package(file_names) #:nodoc:
|
708
|
+
"//{{{\nmerge(config.shadowTiddlers,{\n\n"+
|
709
|
+
((file_names.map do |f|
|
710
|
+
Tiddler.new.from_file(f)
|
711
|
+
end).map do |t|
|
712
|
+
"'" + t.name + "':[\n " +
|
713
|
+
t.text.chomp.dump.gsub(/\\t/,"\t").gsub(/\\n/,"\",\n \"").gsub(/\\#/,"#") + "\n].join(\"\\n\")"
|
714
|
+
end).join(",\n\n")+
|
715
|
+
"\n\n});\n//}}}\n"
|
716
|
+
end
|
717
|
+
|
718
|
+
# copy all tiddlers from another TW file into this TW
|
719
|
+
# good for creating Tiddlyspot flavours
|
720
|
+
def copy_all_tiddlers_from(file_name)
|
721
|
+
TiddlyWiki.new.source_empty(file_name).tiddlers.each do |t|
|
722
|
+
add_tiddler t
|
723
|
+
end
|
724
|
+
end
|
725
|
+
|
726
|
+
# write all tiddlers to files in dir_name
|
727
|
+
def write_all_tiddlers_to(dir_name, div = false)
|
728
|
+
tiddlers.each do |t|
|
729
|
+
|
730
|
+
ext = 'tiddler'
|
731
|
+
|
732
|
+
# TODO improve this
|
733
|
+
if t.tags and t.tags.include? "systemConfig"
|
734
|
+
ext = 'js'
|
735
|
+
elsif t.name =~ /Template/
|
736
|
+
ext = 'html'
|
737
|
+
elsif t.name =~ /(StyleSheet|Styles)/
|
738
|
+
ext = 'css'
|
739
|
+
end
|
740
|
+
|
741
|
+
name = t.name.gsub('/', '%2F')
|
742
|
+
file = "#{dir_name}/#{name}.#{ext}"
|
743
|
+
if div
|
744
|
+
t.to_fields_string(@use_pre).join(' ').to_file("#{file}.div")
|
745
|
+
end
|
746
|
+
t.text.to_file(file)
|
747
|
+
modified = t.modified ? Time.convertFromYYYYMMDDHHMM(t.modified) : Time.now
|
748
|
+
File.utime(modified, modified, file)
|
749
|
+
end
|
750
|
+
end
|
751
|
+
|
752
|
+
end
|
753
|
+
|
754
|
+
#
|
755
|
+
# A short hand for DSL style TiddlyWiki creation. Takes a block of TiddlyWiki methods that get instance_eval'ed
|
756
|
+
#
|
757
|
+
def make_tw(source=nil,&block)
|
758
|
+
tw = TiddlyWiki.new
|
759
|
+
tw.source_empty(source) if source
|
760
|
+
tw.instance_eval(&block) if block
|
761
|
+
tw
|
762
|
+
end
|
763
|
+
|