Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
tree: f915020a4c
Fetching contributors…

Cannot retrieve contributors at this time

executable file 162 lines (139 sloc) 3.957 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
#!/usr/bin/env macruby -w
## Edit Safari bookmarks in iOS Simulator
#
# List existing bookmarks:
# $ simulator_bookmarks list
#
# Add a bookmarklet:
# $ simulator_bookmarks add "Toggle Livereload" -u 'javascript:livereload.toggle()'
#
# Apart from specifying the URL with the "-u" argument, you can also pipe it in via STDIN.
#
# This only affects the simulator running in the "en_US" locale. To edit bookmarks of
# a different locale, use the "-l" option:
#
# $ simulator_bookmarks add "Título" -l es_ES -u http://example.es
#
framework 'Foundation'
require 'fileutils'

class Bookmarks
  SDK_DIR = '/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs'
  GLOB = 'Applications/MobileSafari.app/StaticBookmarks-*.plist'

  def self.simulators
    Dir.entries(SDK_DIR).reject {|s| s.start_with?('.') }
  rescue Errno::ENOENT
    []
  end

  def self.find_by_locale(locale)
    path = GLOB.sub('*', locale)
    simulators.map { |version| File.join(SDK_DIR, version, path) }.select {|f| File.exist? f }
  end

  attr_reader :items, :path

  def initialize(filename)
    @path = filename
    @items, @format = read_plist(path)
  end

  def prepend_bookmark(title, url)
    @items.unshift("Title" => title, "URL" => url)
  end

  def writable?
    File.writable? path
  end

  def short_path
    path.sub(SDK_DIR + '/', '')
  end

  def save
    backup!
    write_plist(path)
  end

  private

  def read_plist(path)
    data = NSData.dataWithContentsOfFile(path)
    format = Pointer.new('Q')
    items = handle_errors { |error|
      NSPropertyListSerialization.propertyListWithData data,
        options:KCFPropertyListMutableContainers, format:format, error:error
    }
    [items, format[0]]
  end

  # KCFPropertyListBinaryFormat_v1_0
  def write_plist(path)
    data = handle_errors { |error|
      NSPropertyListSerialization.dataWithPropertyList @items,
        format:@format, options:0, error:error
    }
    data.writeToFile(path, atomically:false)
  end

  def handle_errors
    errorp = Pointer.new(:object)
    result = yield errorp
    raise errorp[0].userInfo['NSDebugDescription'] if errorp[0]
    result
  end

  def backup!
    bak = path + ".orig"
    FileUtils.cp path, bak unless File.exist? bak
  end
end

if __FILE__ == $0
  extract_arg = ->(flag, default_value = nil) {
    if idx = ARGV.find_index {|a| a.start_with?(flag) }
      loc = ARGV.delete_at(idx).sub(flag, '')
      loc.empty? ? ARGV.delete_at(idx) : loc
    elsif block_given? then yield
    else default_value
    end
  }
  locale = extract_arg.('-l', 'en_US')

  # command
  case ARGV.shift
  when 'add'
    url = extract_arg.('-u') { $stdin.read.strip unless $stdin.tty? }
    title = ARGV.shift

    if url.nil? or url.empty?
      warn "Error: no URL given"
      exit 1
    end

    if title.nil? or title.empty?
      warn "Error: no title given"
      exit 1
    end

    num_edited = 0

    Bookmarks.find_by_locale(locale).each do |file|
      bookmarks = Bookmarks.new file
      unless bookmarks.writable?
        warn "Error: no write permissions to edit boomarks"
        warn "(Try running with sudo)"
        exit 1
      end

      bookmarks.prepend_bookmark title, url
      bookmarks.save
      puts "Edited #{bookmarks.short_path}"
      num_edited += 1
    end

    if num_edited.zero?
      warn "Warning: no bookmarks edited"
      exit 1
    end
  when 'list'
    files = Bookmarks.find_by_locale(locale).each_with_index do |file, idx|
      bookmarks = Bookmarks.new file
      puts if idx > 0
      puts "#{bookmarks.short_path}:"
      for item in bookmarks.items
        url = item['URL'] || item['iPhoneURL']
        title = item['Title'] || item['iPhoneTitle']
        if title and url
          puts " #{title} [#{url}]"
        end
      end
    end

    if files.empty?
      warn "Warning: no bookmarks found"
      exit 1
    end
  else
    warn %(Usage: #{$0} add "My title" -u http://example.com)
    exit 0
  end
end
Something went wrong with that request. Please try again.