├── README.md ├── ipad.pyui ├── iphone.pyui └── webbrowser.py /README.md: -------------------------------------------------------------------------------- 1 | Pythonista-Webbrowser 2 | ===================== 3 | 4 | ![screenshot](https://www.dropbox.com/s/g0bgcnkas9akb29/Pythonista-Webbrowser.PNG?raw=1) 5 | -------------------------------------------------------------------------------- /ipad.pyui: -------------------------------------------------------------------------------- 1 | [{"class":"View","attributes":{"custom_class":"BrowserView","background_color":"RGBA(1.000000,1.000000,1.000000,1.000000)","tint_color":"RGBA(0.000000,0.478000,1.000000,1.000000)","enabled":true,"border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","flex":""},"frame":"{{0, 0}, {768, 960}}","nodes":[{"class":"View","attributes":{"name":"controlpanel","background_color":"RGBA(0.571429,0.826999,1.000000,1.000000)","uuid":"54EBCF19-1981-48B2-BEA5-53E4AFADC8F7","enabled":true,"border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","flex":"WH"},"frame":"{{0, 0}, {768, 960}}","nodes":[{"class":"TextField","attributes":{"font_size":17,"enabled":true,"flex":"W","autocorrection_type":"no","name":"addressbar","border_style":3,"text_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","alignment":"left","border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","spellchecking_type":"no","uuid":"79C0C667-8B3B-466F-89FC-D97E987D7D9E"},"frame":"{{150, 25}, {516, 44}}","nodes":[]},{"class":"Button","attributes":{"tint_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","font_size":15,"enabled":true,"font_bold":false,"name":"go_back","flex":"","border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","action":"","uuid":"BCD3DF37-6E39-4654-AF02-07D21E4ADED2","image_name":"ionicons-ios7-arrow-left-32","title":""},"frame":"{{6, 29}, {40, 40}}","nodes":[]},{"class":"Button","attributes":{"tint_color":"RGBA(0.000000,0.000000,0.000776,1.000000)","font_size":15,"enabled":true,"font_bold":false,"name":"go_forward","flex":"","border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","uuid":"0B9D7DEA-2513-4661-804B-BAA6214C40B4","image_name":"ionicons-ios7-arrow-right-32","title":""},"frame":"{{54, 29}, {40, 40}}","nodes":[]},{"class":"Button","attributes":{"tint_color":"RGBA(1.000000,1.000000,0.000000,1.000000)","font_size":15,"enabled":true,"font_bold":false,"name":"favourite","flex":"L","border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","uuid":"083A778E-DC14-4C5E-9FD0-F2E342A5E3AF","image_name":"ionicons-ios7-star-outline-32","title":""},"frame":"{{674, 29}, {40, 40}}","nodes":[]},{"class":"Button","attributes":{"tint_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","font_size":15,"enabled":true,"font_bold":false,"name":"bookmarks","flex":"","border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","uuid":"61342153-1DC2-4DA0-B0D4-C9C42C9C6A84","image_name":"ionicons-ios7-bookmarks-outline-32","title":""},"frame":"{{102, 29}, {40, 40}}","nodes":[]},{"class":"Button","attributes":{"tint_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","font_size":15,"enabled":true,"font_bold":false,"name":"more","flex":"L","border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","uuid":"D1FD093B-39BC-49D1-AB16-2B074570AA2B","image_name":"ionicons-ios7-more-32","title":""},"frame":"{{722, 29}, {40, 40}}","nodes":[]},{"class":"Button","attributes":{"tint_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","font_size":15,"enabled":true,"font_bold":false,"name":"reload","flex":"L","border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","uuid":"9AE417A4-88F3-416A-9EA5-2036D03F8120","image_name":"ionicons-ios7-refresh-empty-32","title":""},"frame":"{{633, 34}, {32, 32}}","nodes":[]}]},{"class":"WebView","attributes":{"name":"webview","border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","uuid":"8BC1493D-F4A5-4C03-9E07-473AAB300F03","enabled":true,"scales_to_fit":true,"flex":"WH"},"frame":"{{0, 75}, {768, 885}}","nodes":[]}]}] 2 | -------------------------------------------------------------------------------- /iphone.pyui: -------------------------------------------------------------------------------- 1 | [{"class":"View","attributes":{"custom_class":"BrowserView","background_color":"RGBA(1.000000,1.000000,1.000000,1.000000)","tint_color":"RGBA(0.000000,0.478000,1.000000,1.000000)","enabled":true,"border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","flex":""},"frame":"{{0, 0}, {320, 504}}","nodes":[{"class":"View","attributes":{"enabled":true,"flex":"WH","name":"controlpanel","border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","background_color":"RGBA(0.571429,0.828571,1.000000,1.000000)","custom_class":"","uuid":"2317F69C-B80B-4DF9-8FEF-1B9CD411808E"},"frame":"{{0, 0}, {320, 504}}","nodes":[{"class":"TextField","attributes":{"background_color":"RGBA(1.000000,1.000000,1.000000,1.000000)","alignment":"left","border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","font_size":17,"enabled":true,"tint_color":"RGBA(1.000000,1.000000,1.000000,1.000000)","flex":"W","text_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","name":"addressbar","corner_radius":10,"border_style":3,"border_width":0.5,"uuid":"EE27D174-A4DE-4D8B-864F-C7A395ECE068"},"frame":"{{6, 34}, {274, 32}}","nodes":[]},{"class":"Button","attributes":{"tint_color":"RGBA(1.000000,0.960000,0.000000,1.000000)","font_size":15,"enabled":true,"font_bold":false,"name":"favourite","flex":"L","border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","action":"","uuid":"D644296F-B0C5-47E7-9A04-D8FA6F2843C0","image_name":"ionicons-ios7-star-outline-32","title":""},"frame":"{{287, 39}, {27, 27}}","nodes":[]},{"class":"Button","attributes":{"tint_color":"RGBA(0.000000,0.000000,0.003220,1.000000)","font_size":15,"enabled":true,"font_bold":false,"name":"go_back","flex":"T","border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","action":"","uuid":"26A336EA-E736-46B9-9FA2-11185EFDD950","image_name":"ionicons-ios7-arrow-left-32","title":""},"frame":"{{0, 464}, {40, 40}}","nodes":[]},{"class":"Button","attributes":{"tint_color":"RGBA(0.000000,0.000000,0.003220,1.000000)","font_size":15,"enabled":true,"font_bold":false,"name":"go_forward","flex":"LRT","border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","action":"","uuid":"3B2820DE-BBE1-4F3D-A88B-2CE3B47535CD","image_name":"ionicons-ios7-arrow-right-32","title":""},"frame":"{{97, 464}, {40, 40}}","nodes":[]},{"class":"Button","attributes":{"tint_color":"RGBA(0.000000,0.000000,0.003220,1.000000)","font_size":15,"enabled":true,"font_bold":false,"name":"bookmarks","flex":"LRT","border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","uuid":"4A664020-9E8A-4BD4-82F5-995A9C901293","image_name":"ionicons-ios7-bookmarks-outline-32","title":""},"frame":"{{190.5, 464}, {39.5, 40}}","nodes":[]},{"class":"Button","attributes":{"tint_color":"RGBA(0.000000,0.000000,0.003220,1.000000)","font_size":15,"enabled":true,"font_bold":false,"name":"more","flex":"LRT","border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","uuid":"A8853778-3910-4CC7-8F0E-0A192C796061","image_name":"ionicons-ios7-more-32","title":""},"frame":"{{280, 464}, {40, 40}}","nodes":[]},{"class":"Button","attributes":{"tint_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","font_size":15,"enabled":true,"font_bold":false,"name":"reload","flex":"L","border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","action":"","uuid":"CA26B9D3-D623-4DF1-A124-9D0B1E378145","image_name":"ionicons-ios7-refresh-empty-32","title":""},"frame":"{{239, 34}, {40, 32}}","nodes":[]}]},{"class":"WebView","attributes":{"name":"webview","border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","uuid":"D24217DF-B6A1-4BCF-A8FB-CB199E804A20","enabled":true,"scales_to_fit":true,"flex":"WH"},"frame":"{{0, 79}, {320, 380}}","nodes":[]}]}] 2 | -------------------------------------------------------------------------------- /webbrowser.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | import console, json, os, pickle, ui, urlparse 4 | 5 | filename_bookmarks = 'bookmarks.json' 6 | filename_history = 'history.txt' 7 | 8 | class BrowserView (ui.View): 9 | 10 | def evaluate_javascript(self, js): 11 | return self['webview'].evaluate_javascript(js) 12 | 13 | def get_title(self): 14 | return self.evaluate_javascript('document.title') 15 | 16 | def get_url(self): 17 | return self.evaluate_javascript('window.location.href') 18 | 19 | def parse_url(self, url): 20 | return urlparse.urlparse(url).netloc 21 | 22 | def set_url(self, url=None): 23 | url = url or self.get_url() 24 | addr_bar = self['controlpanel']['addressbar'] 25 | if self.addressbar_is_editing: 26 | addr_bar.text = url 27 | addr_bar.alignment = ui.ALIGN_LEFT 28 | else: 29 | addr_bar.text = self.parse_url(url) 30 | addr_bar.alignment = ui.ALIGN_CENTER 31 | 32 | def load_url(self, url): 33 | if '.' not in url: 34 | url = 'http://www.google.com/search?q={}'.format(url.replace(' ', '+')) 35 | elif urlparse.urlparse(url).netloc == '': 36 | url = 'http://'+url 37 | self['webview'].load_url(url) 38 | 39 | def load_bookmarks(self, filename=filename_bookmarks): 40 | try: 41 | with open(filename, 'r+') as f: 42 | bookmarks = json.load(f) 43 | except IOError as e: 44 | bookmarks = {} 45 | with open(filename, 'w+') as f: 46 | json.dump(bookmarks, f, indent=4) 47 | return bookmarks 48 | 49 | def load_history(self, filename=filename_history): 50 | try: 51 | with open(filename, 'r+') as f: 52 | history = pickle.load(f) 53 | except (IOError, IndexError) as e: 54 | history = [] 55 | with open(filename, 'w+') as f: 56 | pickle.dump(history, f) 57 | return history 58 | 59 | def init_buttons(self): 60 | for subview in self['controlpanel'].subviews: 61 | subview.action = self.button_tapped 62 | 63 | def init_addressbar(self): 64 | addressbar = self['controlpanel']['addressbar'] 65 | addressbar.autocapitalization_type = ui.AUTOCAPITALIZE_NONE 66 | addressbar.keyboard_type = ui.KEYBOARD_WEB_SEARCH 67 | addressbar.clear_button_mode = 'while_editing' 68 | addressbar.font = ('', addressbar.height*0.4) 69 | addressbar.delegate = self 70 | addressbar.action = None 71 | 72 | def init_webbrowser(self): 73 | web = self['webview'] 74 | web.load_url('https://omz-forums.appspot.com/pythonista') 75 | web.delegate = self 76 | 77 | def init_size(self): 78 | # initialize with correct size when landscape 79 | orientation = ui.WebView(frame=(0,0,100,200)).eval_js('window.orientation') 80 | if orientation in (-90, 90): 81 | self.frame = (0, 0, self.height, self.width) 82 | 83 | def did_load(self): 84 | self.init_buttons() 85 | self.init_webbrowser() 86 | self.init_addressbar() 87 | self.init_size() 88 | self.flex = 'WH' 89 | self.bookmarks = self.load_bookmarks() 90 | self.history = self.load_history() 91 | self.addressbar_is_editing = False 92 | self.webpage_has_loaded = False 93 | self.favourite_images = {True :ui.Image.named('ionicons-ios7-star-32'), 94 | False:ui.Image.named('ionicons-ios7-star-outline-32')} 95 | 96 | def save_history(self, filename=filename_history): 97 | with open(filename, 'w') as f: 98 | url = self.get_url() 99 | if url in self.history: 100 | self.history.remove(url) 101 | self.history.append(url) 102 | f.seek(0) 103 | pickle.dump(self.history, f) 104 | 105 | def clear_history(self, sender, filename=filename_history): 106 | with open(filename, 'w') as f: 107 | self.history = [] 108 | f.seek(0) 109 | pickle.dump(self.history, f) 110 | sender.superview.superview['history'].data_source.items = self.history 111 | sender.superview.superview['history'].reload() 112 | 113 | def save_bookmark(self, filename=filename_bookmarks): 114 | with open(filename, 'w') as f: 115 | url = self.get_url() 116 | title = self.get_title() or self.parse_url(url) 117 | self.bookmarks[title] = url 118 | f.seek(0) 119 | json.dump(self.bookmarks, f, indent=4) 120 | self['controlpanel']['favourite'].image = self.favourite_images[True] 121 | 122 | def remove_bookmark(self, title=None, filename=filename_bookmarks): 123 | with open(filename, 'w') as f: 124 | title = title or self.get_title() 125 | del self.bookmarks[title] 126 | f.seek(0) 127 | json.dump(self.bookmarks, f, indent=4) 128 | self['controlpanel']['favourite'].image = self.favourite_images[False] 129 | 130 | def popup_menu(self): 131 | popup = ui.View(name='menu', frame=(0, 0, 320, 500)) 132 | 133 | toolbar = ui.View(frame=(-5, 0, 330, 100), name='toolbar') 134 | toolbar.border_width = 0.5 135 | toolbar.border_color = '#B2B2B2' 136 | 137 | label = ui.Label() 138 | label.text = 'Bookmarks' 139 | label.alignment = ui.ALIGN_CENTER 140 | label.frame = (0, 0, 320, 50) 141 | label.name = 'title' 142 | 143 | segment_ctrl = ui.SegmentedControl(name='segctrl') 144 | segment_ctrl.segments = ['Bookmarks', 'History'] 145 | segment_ctrl.width = 170 146 | segment_ctrl.center = popup.center 147 | segment_ctrl.y = label.height 148 | segment_ctrl.selected_index = 0 149 | segment_ctrl.action = self.bookmarks_or_history 150 | 151 | button = ui.Button() 152 | button.frame = (segment_ctrl.x*3.5, segment_ctrl.y, 60, 30) 153 | button.font = ('', 15) 154 | button.title= 'Clear' 155 | button.name = 'clear' 156 | button.action = self.clear_history 157 | button.hidden = True 158 | 159 | toolbar.add_subview(label) 160 | toolbar.add_subview(segment_ctrl) 161 | toolbar.add_subview(button) 162 | 163 | popup.add_subview(toolbar) 164 | data_source = ui.ListDataSource(sorted(self.bookmarks.keys())) 165 | popup.add_subview(self.list_bookmarks_and_history(data_source, width=320,height=toolbar.superview.height-toolbar.height, y=toolbar.height, name='bookmarks')) 166 | x, y = self['controlpanel']['bookmarks'].center 167 | popup.present('popover', popover_location=(x, y), hide_title_bar=True) 168 | 169 | def bookmarks_or_history(self, sender): 170 | toolbar = sender.superview 171 | if sender.selected_index == 0: 172 | toolbar['clear'].hidden = True 173 | toolbar['title'].text = 'Bookmarks' 174 | data_source = ui.ListDataSource(sorted(self.bookmarks.keys())) 175 | tv = self.list_bookmarks_and_history(data_source, width=320, height=toolbar.superview.height-toolbar.height, y=toolbar.height, name='bookmarks') 176 | toolbar.superview.remove_subview(toolbar.superview['history']) 177 | else: 178 | toolbar['clear'].hidden = False 179 | toolbar['title'].text = 'History' 180 | data_source = ui.ListDataSource(self.history[::-1]) 181 | tv = self.list_bookmarks_and_history(data_source, width=320, height=toolbar.superview.height-toolbar.height, y=toolbar.height, name='history') 182 | toolbar.superview['bookmarks'].hidden=True 183 | toolbar.superview.remove_subview(toolbar.superview['bookmarks']) 184 | sender.superview.superview.add_subview(tv) 185 | 186 | def list_bookmarks_and_history(self, data_source, **kwargs): 187 | tv = ui.TableView() 188 | tv.data_source = data_source 189 | tv.delegate = self 190 | for k, v in kwargs.items(): 191 | setattr(tv, k, v) 192 | return tv 193 | 194 | def show_more_menu(self): 195 | popup = ui.TableView() 196 | popup.width = 250 197 | popup.height = 500 198 | popup.name = 'More' 199 | popup.data_source = popup.delegate = self 200 | button = self['controlpanel']['more'] 201 | popup.present('popover', popover_location=(button.x, button.y+button.height)) 202 | 203 | def button_tapped(self, sender): 204 | if sender.name == 'favourite': 205 | if self.get_url() in self.bookmarks.values(): 206 | self.remove_bookmark() 207 | else: 208 | self.save_bookmark() 209 | elif sender.name == 'bookmarks': 210 | self.popup_menu() 211 | elif sender.name == 'more': 212 | self.show_more_menu() 213 | else: 214 | eval("self['webview'].{}()".format(sender.name)) 215 | 216 | def tableview_number_of_rows(self, tableview, section): 217 | if tableview.name == 'Bookmarks': 218 | return len(self.bookmarks) 219 | elif tableview.name == 'More': 220 | return 1 221 | 222 | def tableview_cell_for_row(self, tableview, section, row): 223 | if tableview.name == 'Bookmarks': 224 | cell = ui.TableViewCell() 225 | cell.text_label.text = sorted(self.bookmarks.keys())[row] 226 | cell.image_view.image = ui.Image.named('ionicons-ios7-bookmarks-outline-32') 227 | cell.image_view.tint_color = '#66CCFF' 228 | return cell 229 | elif tableview.name == 'More': 230 | cell = ui.TableViewCell() 231 | cell.text_label.text = 'Settings' 232 | cell.image_view.image = ui.Image.named('ionicons-wrench-32') 233 | return cell 234 | 235 | @ui.in_background 236 | def tableview_did_select(self, tableview, section, row): 237 | if tableview.name == 'bookmarks': 238 | url = self.bookmarks[sorted(self.bookmarks.keys())[row]] 239 | self.load_url(url) 240 | tableview.superview.close() 241 | elif tableview.name == 'history': 242 | url = tableview.data_source.items[row] 243 | tableview.superview.close() 244 | self.load_url(url) 245 | elif tableview.name == 'More': 246 | tableview.close() 247 | console.hud_alert('No settings yet...', 'error', 1) 248 | 249 | def tableview_can_delete(self, tableview, section, row): 250 | return True 251 | 252 | def tableview_delete(self, tableview, section, row): 253 | item = sorted(self.bookmarks.keys())[row] 254 | self.remove_bookmark(item) 255 | tableview.reload() 256 | 257 | def textfield_did_begin_editing(self, textfield): 258 | self.addressbar_is_editing = True 259 | self.set_url() 260 | self['controlpanel']['reload'].hidden = True 261 | 262 | def textfield_did_end_editing(self, textfield): 263 | self.addressbar_is_editing = False 264 | self['controlpanel']['reload'].hidden = False 265 | self.set_url() 266 | 267 | def textfield_should_return(self, textfield): 268 | url = self['controlpanel']['addressbar'].text 269 | self.load_url(url) 270 | textfield.end_editing() 271 | return True 272 | 273 | def webview_did_start_load(self, webview): 274 | self.webpage_has_loaded = False 275 | 276 | def webview_did_finish_load(self, webview): 277 | if not self.addressbar_is_editing: 278 | self.set_url() 279 | self.webpage_has_loaded = True 280 | page_is_bookmarked = unicode(self.get_url()) in self.bookmarks.values() 281 | self['controlpanel']['favourite'].image = self.favourite_images[page_is_bookmarked] 282 | self.save_history() 283 | 284 | view = 'ipad' if ui.get_screen_size()[0] >= 768 else 'iphone' 285 | browser = ui.load_view(view) 286 | browser.present(hide_title_bar=True, style='panel') 287 | --------------------------------------------------------------------------------