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.
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
+