tiddlywiki_cp 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. data/COPYING +674 -0
  2. data/History.txt +4 -0
  3. data/Manifest.txt +68 -0
  4. data/README.txt +3 -0
  5. data/Rakefile +123 -0
  6. data/bin/tiddlywiki_cp +13 -0
  7. data/lib/tiddlywiki_cp/converters.rb +36 -0
  8. data/lib/tiddlywiki_cp/file2file.rb +32 -0
  9. data/lib/tiddlywiki_cp/file2tiddler.rb +42 -0
  10. data/lib/tiddlywiki_cp/r4tw.rb +763 -0
  11. data/lib/tiddlywiki_cp/tiddler2directory.rb +27 -0
  12. data/lib/tiddlywiki_cp/tiddler2file.rb +41 -0
  13. data/lib/tiddlywiki_cp/tiddler2tiddlywiki.rb +29 -0
  14. data/lib/tiddlywiki_cp/tiddler_css.rb +39 -0
  15. data/lib/tiddlywiki_cp/tiddler_html.rb +39 -0
  16. data/lib/tiddlywiki_cp/tiddler_js.rb +39 -0
  17. data/lib/tiddlywiki_cp/tiddlywiki2file.rb +29 -0
  18. data/lib/tiddlywiki_cp/version.rb +24 -0
  19. data/lib/tiddlywiki_cp.rb +378 -0
  20. data/scripts/txt2html +67 -0
  21. data/setup.rb +1585 -0
  22. data/test/content/a +0 -0
  23. data/test/content/a.div +0 -0
  24. data/test/content/b +0 -0
  25. data/test/content/e +3 -0
  26. data/test/content/e.div +1 -0
  27. data/test/content/html_entities.html +6 -0
  28. data/test/content/test_fetch.html +6 -0
  29. data/test/content/universe.html +10522 -0
  30. data/test/r4tw/addtag.rb +93 -0
  31. data/test/r4tw/all.rb +29 -0
  32. data/test/r4tw/createfrom.rb +62 -0
  33. data/test/r4tw/empties/2.1.3.html +7087 -0
  34. data/test/r4tw/empties/2.2.0.beta5.html +8726 -0
  35. data/test/r4tw/fromremote.rb +19 -0
  36. data/test/r4tw/fromurl.rb +28 -0
  37. data/test/r4tw/shadows.rb +27 -0
  38. data/test/r4tw/tiddler.rb +70 -0
  39. data/test/r4tw/tiddlerfromurl.rb +23 -0
  40. data/test/r4tw/tiddlywiki.rb +66 -0
  41. data/test/r4tw/utils.rb +55 -0
  42. data/test/r4tw/withcontent/2.2.0.beta5.html +8739 -0
  43. data/test/r4tw/withcontent/22b5index.html +13523 -0
  44. data/test/r4tw/withcontent/empty2.html +7084 -0
  45. data/test/r4tw/withcontent/nothing.js +1 -0
  46. data/test/r4tw/write_all_tiddlers_to.rb +62 -0
  47. data/test/test_all.rb +8 -0
  48. data/test/test_helper.rb +36 -0
  49. data/test/test_tiddler_css.rb +55 -0
  50. data/test/test_tiddler_html.rb +54 -0
  51. data/test/test_tiddler_js.rb +56 -0
  52. data/test/test_tiddlywiki_cp.rb +341 -0
  53. data/website/files/DefaultTiddlers.tiddler +2 -0
  54. data/website/files/DefaultTiddlers.tiddler.div +1 -0
  55. data/website/files/Introduction.tiddler +12 -0
  56. data/website/files/Introduction.tiddler.div +1 -0
  57. data/website/files/MainMenu.tiddler +1 -0
  58. data/website/files/MainMenu.tiddler.div +1 -0
  59. data/website/files/SiteSubtitle.tiddler +1 -0
  60. data/website/files/SiteSubtitle.tiddler.div +1 -0
  61. data/website/files/SiteTitle.tiddler +1 -0
  62. data/website/files/SiteTitle.tiddler.div +1 -0
  63. data/website/files/Usage.tiddler +55 -0
  64. data/website/files/Usage.tiddler.div +1 -0
  65. data/website/files/WebDAVSavingPlugin.js +234 -0
  66. data/website/files/WebDAVSavingPlugin.js.div +1 -0
  67. data/website/index.html +9144 -0
  68. data/website/index.xml +336 -0
  69. 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,"&amp;").gsub(/</m,"&lt;").gsub(/>/m,"&gt;").gsub(/\"/m,"&quot;")
57
+ end
58
+
59
+ def decodeHTML
60
+ gsub(/&amp;/m,"&").gsub(/&lt;/m,"<").gsub(/&gt;/m,">").gsub(/&quot;/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
+