├── .gitignore ├── .idea ├── .name ├── GIFs.iml ├── codeStyleSettings.xml ├── dictionaries │ └── orta.xml ├── encodings.xml ├── misc.xml ├── modules.xml ├── scopes │ └── scope_settings.xml ├── vcs.xml ├── workspace.xml └── xcode.xml ├── LICENSE ├── README.md ├── design ├── Icon.png ├── Icon.sketch │ ├── Data │ ├── QuickLook │ │ ├── Preview.png │ │ └── Thumbnail.png │ ├── metadata │ └── version └── Icon@2x.png ├── objc ├── .idea │ ├── .name │ ├── dictionaries │ │ └── orta.xml │ ├── encodings.xml │ ├── misc.xml │ ├── modules.xml │ ├── objc.iml │ ├── scopes │ │ └── scope_settings.xml │ ├── vcs.xml │ ├── workspace.xml │ └── xcode.xml ├── GIFs.xcodeproj │ ├── GIFs.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ │ └── GIFs.xccheckout │ │ └── xcuserdata │ │ │ └── orta.xcuserdatad │ │ │ └── xcschemes │ │ │ ├── GIFs.xcscheme │ │ │ └── xcschememanagement.plist │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ ├── xcshareddata │ │ │ └── GIFs.xccheckout │ │ └── xcuserdata │ │ │ └── orta.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ └── xcuserdata │ │ ├── fabio.xcuserdatad │ │ └── xcschemes │ │ │ ├── All The GIFs.xcscheme │ │ │ ├── Install.xcscheme │ │ │ └── xcschememanagement.plist │ │ └── orta.xcuserdatad │ │ ├── xcdebugger │ │ └── Breakpoints.xcbkptlist │ │ └── xcschemes │ │ ├── GIFs.xcscheme │ │ ├── Install.xcscheme │ │ └── xcschememanagement.plist ├── GIFs.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── GIFs.xccheckout ├── GIFs │ ├── AboutController.swift │ ├── DotSlash.png │ ├── GIFs-Bridging-Header.h │ ├── GIFs-Icon.icns │ ├── GIFs-Info.plist │ ├── GIFs-Prefix.pch │ ├── GIFs.entitlements │ ├── Giphy_API_Logo_ForWhite_Trans.gif │ ├── Github.png │ ├── NSString+StringBetweenStrings.h │ ├── NSString+StringBetweenStrings.m │ ├── ORAppDelegate.h │ ├── ORAppDelegate.m │ ├── ORDarkBlueView.h │ ├── ORDarkBlueView.m │ ├── ORGIFActionsController.h │ ├── ORGIFActionsController.m │ ├── ORGIFController.h │ ├── ORGIFController.m │ ├── ORGIFRightClickMenuMaker.h │ ├── ORGIFRightClickMenuMaker.m │ ├── ORImageBrowserView.h │ ├── ORImageBrowserView.m │ ├── ORImageView.h │ ├── ORImageView.m │ ├── ORMenuController.h │ ├── ORMenuController.m │ ├── ORMiddleGreyView.h │ ├── ORMiddleGreyView.m │ ├── ORSearchTextField.h │ ├── ORSearchTextField.m │ ├── ORSimpleSourceListView.h │ ├── ORSimpleSourceListView.m │ ├── ORSourcesController.h │ ├── ORSourcesController.m │ ├── ORStarredSourceController.h │ ├── ORStarredSourceController.m │ ├── ORTintedVisualEffectView.swift │ ├── ORTumblrController.h │ ├── ORTumblrController.m │ ├── ORWebViewDelegateStuff.h │ ├── ORWebViewDelegateStuff.m │ ├── ORWindowDraggableVisualEffectsView.h │ ├── ORWindowDraggableVisualEffectsView.m │ ├── ORWindowTitleDecorationController.swift │ ├── Reddit.png │ ├── Reddit@2x.png │ ├── RedditWhite.png │ ├── RedditWhite@2x.png │ ├── SBSystemPreferences.h │ ├── Search.png │ ├── Search@2x.png │ ├── SearchWhite.png │ ├── SearchWhite@2x.png │ ├── ShadowView.swift │ ├── TwitterBird.png │ ├── about0.jpg │ ├── about1.jpg │ ├── about10.jpg │ ├── about11.jpg │ ├── about12.jpg │ ├── about13.jpg │ ├── about14.jpg │ ├── about2.jpg │ ├── about3.jpg │ ├── about4.jpg │ ├── about5.jpg │ ├── about6.jpg │ ├── about7.jpg │ ├── about8.jpg │ ├── about9.jpg │ ├── close.png │ ├── close@2x.png │ ├── close_active.png │ ├── close_active@2x.png │ ├── en.lproj │ │ ├── Credits.rtf │ │ ├── InfoPlist.strings │ │ └── MainMenu.xib │ ├── gif_template.html │ ├── main.m │ ├── more.png │ ├── plus_active.png │ ├── plus_active@2x.png │ ├── plus_active@2x.pxm │ ├── shadow.png │ ├── tick.png │ ├── tick@2x.png │ ├── tick_active.png │ ├── tick_active@2x.png │ ├── tumblr_t.png │ ├── tumblr_t@2x.png │ ├── tumblr_t_active.png │ └── tumblr_t_active@2x.png ├── Gemfile ├── Gemfile.lock ├── Makefile ├── Podfile └── Podfile.lock └── web ├── GIFs.app.zip └── yosemite.png /.gitignore: -------------------------------------------------------------------------------- 1 | objc/pods 2 | xcuserdata 3 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | GIFs -------------------------------------------------------------------------------- /.idea/GIFs.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/codeStyleSettings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /.idea/dictionaries/orta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/scopes/scope_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/workspace.xml: -------------------------------------------------------------------------------- 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 | 34 | 35 | 36 | 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 | 92 | 93 | 96 | 97 | 98 | 112 | 113 | 114 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | Classes 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 168 | 169 | 172 | 173 | 174 | 175 | 178 | 179 | 182 | 183 | 186 | 187 | 188 | 189 | 192 | 193 | 196 | 197 | 200 | 201 | 204 | 205 | 206 | 207 | 210 | 211 | 214 | 215 | 216 | 217 | 220 | 221 | 224 | 225 | 228 | 229 | 230 | 231 | 234 | 235 | 238 | 239 | 242 | 243 | 246 | 247 | 248 | 249 | 252 | 253 | 256 | 257 | 260 | 261 | 264 | 265 | 268 | 269 | 270 | 271 | 274 | 275 | 278 | 279 | 282 | 283 | 286 | 287 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 345 | 346 | 347 | 348 | 1358688222820 349 | 1358688222820 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 379 | 380 | 391 | 428 | 429 | 430 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | -------------------------------------------------------------------------------- /.idea/xcode.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, orta therox 2 | All rights reserved. 3 | 4 | This is a customized BSD license, you don't have to write that you've 5 | used my code in your released apps. Also by using this code you agree 6 | to not wholesale copy my app; I love competition, but I'd rather compete 7 | on user experience. Thanks. 8 | 9 | Redistribution and use in source and binary forms, with or without 10 | modification, are permitted provided that the following conditions are met: 11 | 12 | 1. Redistributions of source code must retain the above copyright notice, this 13 | list of conditions and the following disclaimer. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 19 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | 26 | The views and conclusions contained in the software and documentation are those 27 | of the authors and should not be interpreted as representing official policies, 28 | either expressed or implied, of the FreeBSD Project. 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2012's word of the year: GIF. 2 | -------- 3 | 4 | See https://github.com/manmal/GIFs for an updated build 5 | 6 | ![Screenshot](https://raw.github.com/orta/GIFs/master/web/yosemite.png "screenshot") 7 | 8 | ### Download 9 | 10 | You can grab the app from [Mac App Store](https://itunes.apple.com/us/app/gifs/id961850017?l=en&mt=12) (it's free! 🎉) or download the latest version directly [here](https://raw.github.com/orta/GIFs/master/web/GIFs.app.zip). 11 | 12 | ### Compiling 13 | 14 | ``` 15 | git clone https://github.com/orta/GIFs.git 16 | cd GIFs/objc 17 | make bootstrap 18 | ``` 19 | -------------------------------------------------------------------------------- /design/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orta/GIFs/de70a076ca6c695319b047f4cd002b6e162b61f9/design/Icon.png -------------------------------------------------------------------------------- /design/Icon.sketch/Data: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orta/GIFs/de70a076ca6c695319b047f4cd002b6e162b61f9/design/Icon.sketch/Data -------------------------------------------------------------------------------- /design/Icon.sketch/QuickLook/Preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orta/GIFs/de70a076ca6c695319b047f4cd002b6e162b61f9/design/Icon.sketch/QuickLook/Preview.png -------------------------------------------------------------------------------- /design/Icon.sketch/QuickLook/Thumbnail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orta/GIFs/de70a076ca6c695319b047f4cd002b6e162b61f9/design/Icon.sketch/QuickLook/Thumbnail.png -------------------------------------------------------------------------------- /design/Icon.sketch/metadata: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | app 6 | com.bohemiancoding.sketch 7 | build 8 | 5355 9 | commit 10 | b7d299b0a34651d1a0e066786b75aa36168d5809 11 | fonts 12 | 13 | AvenirNext-Bold 14 | 15 | length 16 | 11705 17 | version 18 | 18 19 | 20 | 21 | -------------------------------------------------------------------------------- /design/Icon.sketch/version: -------------------------------------------------------------------------------- 1 | 18 -------------------------------------------------------------------------------- /design/Icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orta/GIFs/de70a076ca6c695319b047f4cd002b6e162b61f9/design/Icon@2x.png -------------------------------------------------------------------------------- /objc/.idea/.name: -------------------------------------------------------------------------------- 1 | All The GIFs -------------------------------------------------------------------------------- /objc/.idea/dictionaries/orta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /objc/.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /objc/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | C/C++ 12 | 13 | 14 | CSS 15 | 16 | 17 | Code style issuesJavaScript 18 | 19 | 20 | Data flow analysisC/C++ 21 | 22 | 23 | JavaScript 24 | 25 | 26 | 27 | 28 | C/C++ 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 52 | 53 | 54 | 55 | 56 | 57 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /objc/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /objc/.idea/objc.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /objc/.idea/scopes/scope_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /objc/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /objc/.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 13 | 14 | 15 | 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 | 51 | 52 | 53 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 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 | 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 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 1406889306032 186 | 1406889306032 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 216 | 217 | 220 | 221 | 222 | 223 | 224 | 225 | file://$PROJECT_DIR$/../../GIFKit/Pod/Classes/GIF+RedditDictionary.m 226 | 57 227 | 228 | 229 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | -------------------------------------------------------------------------------- /objc/.idea/xcode.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /objc/GIFs.xcodeproj/GIFs.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /objc/GIFs.xcodeproj/GIFs.xcodeproj/project.xcworkspace/xcshareddata/GIFs.xccheckout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDESourceControlProjectFavoriteDictionaryKey 6 | 7 | IDESourceControlProjectIdentifier 8 | 18C57799-D27F-48EA-A71E-4FA79879A9D6 9 | IDESourceControlProjectName 10 | GIFs 11 | IDESourceControlProjectOriginsDictionary 12 | 13 | 9D352ECE-BDB8-47FB-ACC6-8421F7FCFF26 14 | https://github.com/orta/GIFs.git 15 | 16 | IDESourceControlProjectPath 17 | objc/GIFs.xcodeproj/project.xcworkspace 18 | IDESourceControlProjectRelativeInstallPathDictionary 19 | 20 | 9D352ECE-BDB8-47FB-ACC6-8421F7FCFF26 21 | ../../.. 22 | 23 | IDESourceControlProjectURL 24 | https://github.com/orta/GIFs.git 25 | IDESourceControlProjectVersion 26 | 110 27 | IDESourceControlProjectWCCIdentifier 28 | 9D352ECE-BDB8-47FB-ACC6-8421F7FCFF26 29 | IDESourceControlProjectWCConfigurations 30 | 31 | 32 | IDESourceControlRepositoryExtensionIdentifierKey 33 | public.vcs.git 34 | IDESourceControlWCCIdentifierKey 35 | 9D352ECE-BDB8-47FB-ACC6-8421F7FCFF26 36 | IDESourceControlWCCName 37 | GIFs 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /objc/GIFs.xcodeproj/GIFs.xcodeproj/xcuserdata/orta.xcuserdatad/xcschemes/GIFs.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 51 | 52 | 58 | 59 | 60 | 61 | 62 | 63 | 69 | 70 | 76 | 77 | 78 | 79 | 81 | 82 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /objc/GIFs.xcodeproj/GIFs.xcodeproj/xcuserdata/orta.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | GIFs.xcscheme 8 | 9 | orderHint 10 | 3 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 60ECD1AE16A1FA6400A0FB7A 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /objc/GIFs.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /objc/GIFs.xcodeproj/project.xcworkspace/xcshareddata/GIFs.xccheckout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDESourceControlProjectFavoriteDictionaryKey 6 | 7 | IDESourceControlProjectIdentifier 8 | DFBD1451-D7EB-40D8-AB51-9C6553F48E07 9 | IDESourceControlProjectName 10 | GIFs 11 | IDESourceControlProjectOriginsDictionary 12 | 13 | 0C026FFCE33D1340AF39A8387EB741DBFB2E627F 14 | https://github.com/orta/GIFs.git 15 | 16 | IDESourceControlProjectPath 17 | objc/GIFs.xcodeproj 18 | IDESourceControlProjectRelativeInstallPathDictionary 19 | 20 | 0C026FFCE33D1340AF39A8387EB741DBFB2E627F 21 | ../../.. 22 | 23 | IDESourceControlProjectURL 24 | https://github.com/orta/GIFs.git 25 | IDESourceControlProjectVersion 26 | 111 27 | IDESourceControlProjectWCCIdentifier 28 | 0C026FFCE33D1340AF39A8387EB741DBFB2E627F 29 | IDESourceControlProjectWCConfigurations 30 | 31 | 32 | IDESourceControlRepositoryExtensionIdentifierKey 33 | public.vcs.git 34 | IDESourceControlWCCIdentifierKey 35 | 0C026FFCE33D1340AF39A8387EB741DBFB2E627F 36 | IDESourceControlWCCName 37 | GIFs 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /objc/GIFs.xcodeproj/project.xcworkspace/xcuserdata/orta.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orta/GIFs/de70a076ca6c695319b047f4cd002b6e162b61f9/objc/GIFs.xcodeproj/project.xcworkspace/xcuserdata/orta.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /objc/GIFs.xcodeproj/xcuserdata/fabio.xcuserdatad/xcschemes/All The GIFs.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 51 | 52 | 58 | 59 | 60 | 61 | 62 | 63 | 69 | 70 | 76 | 77 | 78 | 79 | 81 | 82 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /objc/GIFs.xcodeproj/xcuserdata/fabio.xcuserdatad/xcschemes/Install.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 42 | 43 | 44 | 45 | 51 | 52 | 54 | 55 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /objc/GIFs.xcodeproj/xcuserdata/fabio.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | All The GIFs.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | Install.xcscheme 13 | 14 | orderHint 15 | 6 16 | 17 | 18 | SuppressBuildableAutocreation 19 | 20 | 60ECD1AE16A1FA6400A0FB7A 21 | 22 | primary 23 | 24 | 25 | E59DFC3518EB20980014EDA0 26 | 27 | primary 28 | 29 | 30 | E59DFC3918EB20C90014EDA0 31 | 32 | primary 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /objc/GIFs.xcodeproj/xcuserdata/orta.xcuserdatad/xcdebugger/Breakpoints.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /objc/GIFs.xcodeproj/xcuserdata/orta.xcuserdatad/xcschemes/GIFs.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 46 | 47 | 53 | 54 | 55 | 56 | 65 | 67 | 73 | 74 | 75 | 76 | 77 | 78 | 84 | 86 | 92 | 93 | 94 | 95 | 97 | 98 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /objc/GIFs.xcodeproj/xcuserdata/orta.xcuserdatad/xcschemes/Install.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 42 | 43 | 44 | 45 | 51 | 52 | 54 | 55 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /objc/GIFs.xcodeproj/xcuserdata/orta.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | GIFs.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | Install.xcscheme 13 | 14 | orderHint 15 | 1 16 | 17 | 18 | SuppressBuildableAutocreation 19 | 20 | 60ECD1AE16A1FA6400A0FB7A 21 | 22 | primary 23 | 24 | 25 | E59DFC3918EB20C90014EDA0 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /objc/GIFs.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /objc/GIFs.xcworkspace/xcshareddata/GIFs.xccheckout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDESourceControlProjectFavoriteDictionaryKey 6 | 7 | IDESourceControlProjectIdentifier 8 | 41CA408B-1D1F-492F-856B-C84128DAD663 9 | IDESourceControlProjectName 10 | GIFs 11 | IDESourceControlProjectOriginsDictionary 12 | 13 | 0C026FFCE33D1340AF39A8387EB741DBFB2E627F 14 | https://github.com/orta/GIFs.git 15 | 16 | IDESourceControlProjectPath 17 | objc/GIFs.xcworkspace 18 | IDESourceControlProjectRelativeInstallPathDictionary 19 | 20 | 0C026FFCE33D1340AF39A8387EB741DBFB2E627F 21 | ../.. 22 | 23 | IDESourceControlProjectURL 24 | https://github.com/orta/GIFs.git 25 | IDESourceControlProjectVersion 26 | 111 27 | IDESourceControlProjectWCCIdentifier 28 | 0C026FFCE33D1340AF39A8387EB741DBFB2E627F 29 | IDESourceControlProjectWCConfigurations 30 | 31 | 32 | IDESourceControlRepositoryExtensionIdentifierKey 33 | public.vcs.git 34 | IDESourceControlWCCIdentifierKey 35 | 0C026FFCE33D1340AF39A8387EB741DBFB2E627F 36 | IDESourceControlWCCName 37 | GIFs 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /objc/GIFs/AboutController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AboutController.swift 3 | // GIFs 4 | // 5 | // Created by Orta on 8/31/14. 6 | // Copyright (c) 2014 Orta Therox. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import AppKit 11 | 12 | class AboutController : NSObject { 13 | 14 | @IBOutlet weak var subtitleField: NSTextField! 15 | @IBOutlet weak var aboutWindow: NSWindow! 16 | @IBOutlet weak var mainWindow: NSWindow! 17 | @IBOutlet weak var backgroundImageView: NSImageView! 18 | 19 | @IBOutlet weak var titleBackgroundView: NSView! 20 | 21 | override func awakeFromNib() { 22 | titleBackgroundView.layer!.backgroundColor = NSColor(calibratedWhite: 0, alpha: 0.3).CGColor 23 | } 24 | 25 | @IBAction func openModal(sender: AnyObject) { 26 | 27 | let rando:UInt32 = arc4random_uniform(13) 28 | backgroundImageView.image = NSImage(named: "about\(rando)") 29 | 30 | mainWindow.beginSheet(aboutWindow, completionHandler:nil) 31 | } 32 | 33 | 34 | @IBAction func closeModal(sender: AnyObject) { 35 | mainWindow.endSheet(aboutWindow) 36 | } 37 | 38 | @IBAction func openGithub(sender: AnyObject) { 39 | let workspace = NSWorkspace.sharedWorkspace() 40 | workspace.openURL(NSURL(string: "http://github.com/orta")!); 41 | } 42 | 43 | @IBAction func openTwitter(sender: AnyObject) { 44 | let workspace = NSWorkspace.sharedWorkspace() 45 | if let tweetbot = workspace.fullPathForApplication("Tweetbot") { 46 | workspace.openURL(NSURL(string: "tweetbot:///user_profile/orta")!); 47 | 48 | } else { 49 | workspace.openURL(NSURL(string: "http://twitter.com/orta")!); 50 | } 51 | 52 | } 53 | 54 | @IBAction func openMySite(sender: AnyObject) { 55 | let workspace = NSWorkspace.sharedWorkspace() 56 | workspace.openURL(NSURL(string: "http://orta.io")!); 57 | 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /objc/GIFs/DotSlash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orta/GIFs/de70a076ca6c695319b047f4cd002b6e162b61f9/objc/GIFs/DotSlash.png -------------------------------------------------------------------------------- /objc/GIFs/GIFs-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | #import 6 | #import -------------------------------------------------------------------------------- /objc/GIFs/GIFs-Icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orta/GIFs/de70a076ca6c695319b047f4cd002b6e162b61f9/objc/GIFs/GIFs-Icon.icns -------------------------------------------------------------------------------- /objc/GIFs/GIFs-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIconFile 10 | GIFs-Icon 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleURLTypes 24 | 25 | 26 | CFBundleTypeRole 27 | Editor 28 | CFBundleURLSchemes 29 | 30 | gifs-app 31 | 32 | 33 | 34 | CFBundleVersion 35 | 1.1.0 36 | LSApplicationCategoryType 37 | public.app-category.entertainment 38 | LSMinimumSystemVersion 39 | ${MACOSX_DEPLOYMENT_TARGET} 40 | NSAppTransportSecurity 41 | 42 | NSAllowsArbitraryLoads 43 | 44 | 45 | NSHumanReadableCopyright 46 | Copyright © 2014 Orta Therox. All rights reserved. 47 | NSMainNibFile 48 | MainMenu 49 | NSPrincipalClass 50 | NSApplication 51 | 52 | 53 | -------------------------------------------------------------------------------- /objc/GIFs/GIFs-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'GIFs' target in the 'GIFs' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #import 8 | #endif 9 | -------------------------------------------------------------------------------- /objc/GIFs/GIFs.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.files.user-selected.read-write 8 | 9 | com.apple.security.network.client 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /objc/GIFs/Giphy_API_Logo_ForWhite_Trans.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orta/GIFs/de70a076ca6c695319b047f4cd002b6e162b61f9/objc/GIFs/Giphy_API_Logo_ForWhite_Trans.gif -------------------------------------------------------------------------------- /objc/GIFs/Github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orta/GIFs/de70a076ca6c695319b047f4cd002b6e162b61f9/objc/GIFs/Github.png -------------------------------------------------------------------------------- /objc/GIFs/NSString+StringBetweenStrings.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+StringBetweenStrings.h 3 | // Artsy Folio 4 | // 5 | // Created by orta therox on 03/10/2012. 6 | // Copyright (c) 2012 http://art.sy. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSString (StringBetweenStrings) 12 | 13 | // on a string "hello world" with arguments "e" and "d" 14 | // will return "llo world" or nil if it can't find the start or end. 15 | 16 | - (NSString *)substringBetween:(NSString *)start and:(NSString *)end; 17 | @end 18 | -------------------------------------------------------------------------------- /objc/GIFs/NSString+StringBetweenStrings.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+StringBetweenStrings.m 3 | // Artsy Folio 4 | // 5 | // Created by orta therox on 03/10/2012. 6 | // Copyright (c) 2012 http://art.sy. All rights reserved. 7 | // 8 | 9 | #import "NSString+StringBetweenStrings.h" 10 | 11 | @implementation NSString (StringBetweenStrings) 12 | 13 | - (NSString *)substringBetween:(NSString *)start and:(NSString *)end { 14 | NSRange startingRange = [self rangeOfString:start]; 15 | NSRange endingRange = [self rangeOfString:end]; 16 | 17 | if (startingRange.location == NSNotFound || endingRange.location == NSNotFound) { 18 | return nil; 19 | } 20 | 21 | NSInteger length = endingRange.location - startingRange.location - startingRange.length; 22 | if (length < 0) { 23 | NSLog(@"length is below zero, you need a more specific end result"); 24 | return nil; 25 | } 26 | 27 | NSInteger location = startingRange.location + startingRange.length; 28 | 29 | if (length + location > self.length || location < 0) { 30 | return nil; 31 | } 32 | 33 | return [self substringWithRange:NSMakeRange(location, length)]; 34 | } 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /objc/GIFs/ORAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // ORAppDelegate.h 3 | // GIFs 4 | // 5 | // Created by orta therox on 12/01/2013. 6 | // Copyright (c) 2013 Orta Therox. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class ORMenuController; 12 | @interface ORAppDelegate : NSObject 13 | 14 | @property (weak) IBOutlet NSProgressIndicator *networkProgress; 15 | @property (assign) IBOutlet NSWindow *window; 16 | @property (strong) IBOutlet ORMenuController *menuController; 17 | 18 | + (void)setNetworkActivity:(BOOL)activity; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /objc/GIFs/ORAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // ORAppDelegate.m 3 | // GIFs 4 | // 5 | // Created by orta therox on 12/01/2013. 6 | // Copyright (c) 2013 Orta Therox. All rights reserved. 7 | // 8 | 9 | #import "ORAppDelegate.h" 10 | #import "ORMenuController.h" 11 | 12 | @implementation ORAppDelegate 13 | 14 | static ORAppDelegate *_sharedInstance = nil; 15 | 16 | - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { 17 | _sharedInstance = self; 18 | 19 | self.window.appearance = [NSAppearance appearanceNamed:NSAppearanceNameVibrantLight]; 20 | self.window.titleVisibility = NSWindowTitleHidden; 21 | self.window.titlebarAppearsTransparent = YES; 22 | self.window.movableByWindowBackground = YES; 23 | 24 | // They get cropped 25 | // [self moveTitleBarButtons]; 26 | 27 | [self.menuController.menuTableView reloadData]; 28 | [self.menuController.menuTableView selectDefaultItem]; 29 | } 30 | 31 | - (void)moveTitlebarButtons 32 | { 33 | 34 | NSButton *closeButton = [self.window standardWindowButton:NSWindowCloseButton]; 35 | NSButton *zoomButton = [self.window standardWindowButton:NSWindowZoomButton]; 36 | NSButton *minimizeButton = [self.window standardWindowButton:NSWindowMiniaturizeButton]; 37 | 38 | closeButton.frame = NSMakeRect(closeButton.frame.origin.x+5, 39 | closeButton.frame.origin.y-12, 40 | closeButton.frame.size.height, 41 | closeButton.frame.size.width); 42 | 43 | zoomButton.frame = NSMakeRect(zoomButton.frame.origin.x+5, 44 | zoomButton.frame.origin.y-12, 45 | zoomButton.frame.size.height, 46 | zoomButton.frame.size.width); 47 | 48 | minimizeButton.frame = NSMakeRect(minimizeButton.frame.origin.x+5, 49 | minimizeButton.frame.origin.y-12, 50 | minimizeButton.frame.size.height, 51 | minimizeButton.frame.size.width); 52 | } 53 | 54 | - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender { 55 | return YES; 56 | } 57 | 58 | + (void)setNetworkActivity:(BOOL)activity 59 | { 60 | if (activity) { 61 | [_sharedInstance.networkProgress startAnimation:self]; 62 | } else { 63 | [_sharedInstance.networkProgress stopAnimation:self]; 64 | } 65 | } 66 | 67 | @end 68 | -------------------------------------------------------------------------------- /objc/GIFs/ORDarkBlueView.h: -------------------------------------------------------------------------------- 1 | // 2 | // ORDarkBlueView.h 3 | // GIFs 4 | // 5 | // Created by Orta on 14/11/2013. 6 | // Copyright (c) 2013 Orta Therox. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ORDarkBlueView : NSView 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /objc/GIFs/ORDarkBlueView.m: -------------------------------------------------------------------------------- 1 | // 2 | // ORDarkBlueView.m 3 | // GIFs 4 | // 5 | // Created by Orta on 14/11/2013. 6 | // Copyright (c) 2013 Orta Therox. All rights reserved. 7 | // 8 | 9 | #import "ORDarkBlueView.h" 10 | 11 | @implementation ORDarkBlueView 12 | 13 | - (void)drawRect:(NSRect)dirtyRect { 14 | [[NSColor colorWithCalibratedRed:0.150 green:0.140 blue:0.169 alpha:1.000] setFill]; 15 | NSRectFill(dirtyRect); 16 | [super drawRect:dirtyRect]; 17 | } 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /objc/GIFs/ORGIFActionsController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ORGIFActionsController.h 3 | // GIFs 4 | // 5 | // Created by Bryan Luby on 5/25/15. 6 | // Copyright (c) 2015 Orta Therox. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /** 12 | * Helper class for shared contextual menu and keyboard shorcut actions on GIFs. 13 | */ 14 | @interface ORGIFActionsController : NSObject 15 | 16 | + (void)tweetOutLinkToURL:(NSURL *)url; 17 | 18 | + (void)tweetOrtaLinkToURL:(NSURL *)url; 19 | 20 | + (void)copyGIFDownloadURLToClipboard:(NSURL *)downloadURL; 21 | 22 | + (void)copyGIFMarkdownToClipboardWithSourceTitle:(NSString *)sourceTitle downloadURL:(NSURL *)downloadURL; 23 | 24 | + (void)openGIFDownloadURLInBrowser:(NSURL *)downloadURL; 25 | 26 | + (void)openGIFContextURLInBrowser:(NSURL *)contextURL; 27 | 28 | + (void)downloadGIFWithURL:(NSURL *)downloadURL completion:(void(^)(BOOL success, NSError *error))completion; 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /objc/GIFs/ORGIFActionsController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ORGIFActionsController.m 3 | // GIFs 4 | // 5 | // Created by Bryan Luby on 5/25/15. 6 | // Copyright (c) 2015 Orta Therox. All rights reserved. 7 | // 8 | 9 | #import "ORGIFActionsController.h" 10 | #import 11 | #import 12 | #import 13 | 14 | @implementation ORGIFActionsController 15 | 16 | + (void)tweetOrtaLinkToURL:(NSURL *)url 17 | { 18 | NSString *tweet = [NSString stringWithFormat:@"@orta - %@", url.absoluteString]; 19 | [self tweet:tweet]; 20 | } 21 | 22 | + (void)tweetOutLinkToURL:(NSURL *)url 23 | { 24 | NSString *tweet = [NSString stringWithFormat:@"%@", url.absoluteString]; 25 | [self tweet:tweet]; 26 | } 27 | 28 | + (void)tweet:(NSString *)tweet 29 | { 30 | GifsKeys *keys = [[GifsKeys alloc] init]; 31 | STTwitterAPI *twitter = [STTwitterAPI twitterAPIWithOAuthConsumerKey: [keys randoTwitterBotConsumerKey] 32 | consumerSecret: [keys randoTwitterBotConsumerSecret] 33 | oauthToken: [keys randoTwitterBotOAuthToken] 34 | oauthTokenSecret: [keys randoTwitterBotOAuthTokenSecret]]; 35 | 36 | [twitter postStatusUpdate:tweet inReplyToStatusID:nil latitude:nil longitude:nil placeID:@"GIFS.app" displayCoordinates:nil trimUser:@0 successBlock:^(NSDictionary *status) { 37 | 38 | } errorBlock:^(NSError *error) { 39 | 40 | }]; 41 | } 42 | 43 | + (void)copyGIFDownloadURLToClipboard:(NSURL *)downloadURL 44 | { 45 | [[NSPasteboard generalPasteboard] clearContents]; 46 | [[NSPasteboard generalPasteboard] writeObjects:@[downloadURL.absoluteString]]; 47 | } 48 | 49 | + (void)copyGIFMarkdownToClipboardWithSourceTitle:(NSString *)sourceTitle downloadURL:(NSURL *)downloadURL 50 | { 51 | [[NSPasteboard generalPasteboard] clearContents]; 52 | NSString *markdown = [NSString stringWithFormat:@"![%@](%@)", sourceTitle, downloadURL]; 53 | [[NSPasteboard generalPasteboard] writeObjects:@[markdown]]; 54 | } 55 | 56 | + (void)openGIFDownloadURLInBrowser:(NSURL *)downloadURL 57 | { 58 | [self openURL:downloadURL]; 59 | } 60 | 61 | + (void)openGIFContextURLInBrowser:(NSURL *)contextURL 62 | { 63 | [self openURL:contextURL]; 64 | } 65 | 66 | + (void)openURL:(NSURL *)URL 67 | { 68 | [[NSWorkspace sharedWorkspace] openURL:URL]; 69 | } 70 | 71 | + (void)downloadGIFWithURL:(NSURL *)downloadURL completion:(void(^)(BOOL success, NSError *error))completion 72 | { 73 | NSSavePanel *savePanel = [NSSavePanel savePanel]; 74 | savePanel.allowedFileTypes = @[@"gif"]; 75 | savePanel.canCreateDirectories = YES; 76 | savePanel.allowsOtherFileTypes = NO; 77 | savePanel.canSelectHiddenExtension = YES; 78 | 79 | NSString *downloadDirectoryPath = [ NSSearchPathForDirectoriesInDomains(NSDownloadsDirectory, NSUserDomainMask, YES) firstObject]; 80 | NSURL *downloadDirectoryURL = [NSURL fileURLWithPath:downloadDirectoryPath]; 81 | static NSURL * lastUserSelectedDirectoryURL = nil; 82 | savePanel.directoryURL = lastUserSelectedDirectoryURL ? lastUserSelectedDirectoryURL : downloadDirectoryURL; 83 | savePanel.nameFieldStringValue = downloadURL.lastPathComponent; 84 | 85 | NSURLRequest *urlRequest = [[NSURLRequest alloc] initWithURL:downloadURL]; 86 | AFHTTPRequestOperation *afhttpRequestOperation = [[AFHTTPRequestOperation alloc] initWithRequest:urlRequest]; 87 | [afhttpRequestOperation 88 | setDownloadProgressBlock:^(NSUInteger bytesRead, long long int totalBytesRead, long long int totalBytesExpectedToRead) { 89 | 90 | }]; 91 | 92 | [savePanel beginWithCompletionHandler:^(NSInteger result) { 93 | if (result) { 94 | lastUserSelectedDirectoryURL = savePanel.directoryURL; 95 | 96 | [afhttpRequestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, NSData *responseObject) { 97 | BOOL success = [responseObject writeToURL:savePanel.URL atomically:YES]; 98 | 99 | NSError *error; 100 | if (!success) { 101 | NSString *errorString = NSLocalizedString(@"Failed to save file", @"Failed to save file"); 102 | error = [NSError errorWithDomain:NSCocoaErrorDomain 103 | code:NSFileWriteUnknownError 104 | userInfo:@{NSLocalizedDescriptionKey: errorString}]; 105 | NSLog(@"%@", errorString); 106 | } else { 107 | NSLog(@"Successfully downloaded file."); 108 | } 109 | 110 | if (completion) { 111 | completion(success, error); 112 | } 113 | } failure:^(AFHTTPRequestOperation *operation, NSError *error) { 114 | NSLog(@"Fetch failed\n %@ \n %@", operation, error); 115 | 116 | if (completion) { 117 | completion(NO, error); 118 | } 119 | }]; 120 | 121 | [afhttpRequestOperation start]; 122 | } 123 | }]; 124 | } 125 | 126 | @end 127 | -------------------------------------------------------------------------------- /objc/GIFs/ORGIFController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ORGIFController.h 3 | // GIFs 4 | // 5 | // Created by orta therox on 13/01/2013. 6 | // Copyright (c) 2013 Orta Therox. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "ORSimpleSourceListView.h" 12 | #import 13 | #import 14 | #import "ORImageBrowserView.h" 15 | 16 | @class GIF, ORSubredditNetworkModel, ORGiphyNetworkModel, ORTumblrController, ORStarredSourceController, ORMenuController; 17 | 18 | @interface ORGIFController : NSObject 19 | 20 | @property (weak) IBOutlet IKImageBrowserView *collectionView; 21 | 22 | @property (weak) IBOutlet WebView *webView; 23 | @property (weak) IBOutlet NSButton *openGIFContextButton; 24 | 25 | @property (weak) IBOutlet NSPopover *createSourcePopover; 26 | 27 | @property (weak) IBOutlet NSProgressIndicator *downloadProgressIndicator; 28 | 29 | @property (weak) IBOutlet ORSubredditNetworkModel *redditController; 30 | @property (weak) IBOutlet ORGiphyNetworkModel *searchController; 31 | @property (weak) IBOutlet ORTumblrController *tumblrController; 32 | @property (weak) IBOutlet ORStarredSourceController *starredController; 33 | @property (weak) IBOutlet ORMenuController *menuController; 34 | 35 | @property (weak) IBOutlet NSTextField *sectionTitle; 36 | @property (weak) IBOutlet NSTextField *gifTitle; 37 | 38 | @property (strong) GIF *currentGIF; 39 | 40 | - (void)gotNewGIFs; 41 | 42 | - (void)getGIFsFromSourceString:(NSString *)string; 43 | 44 | - (NSString *)gifFilePath; 45 | 46 | - (IBAction)openCurrentGIF:(NSButton *)sender; 47 | 48 | @end 49 | -------------------------------------------------------------------------------- /objc/GIFs/ORGIFController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ORGIFController.m 3 | // GIFs 4 | // 5 | // Created by orta therox on 13/01/2013. 6 | // Copyright (c) 2013 Orta Therox. All rights reserved. 7 | // 8 | 9 | #import "ORGIFController.h" 10 | #import 11 | #import 12 | #import 13 | #import "ORTumblrController.h" 14 | #import "ORStarredSourceController.h" 15 | #import 16 | #import "NSString+StringBetweenStrings.h" 17 | #import "ORMenuController.h" 18 | #import "GIFs-Swift.h" 19 | #import "ORGIFRightClickMenuMaker.h" 20 | #import 21 | 22 | @interface GIF() 23 | @property (nonatomic, strong, readwrite) NSDate *dateAdded; 24 | @end 25 | 26 | @interface ORGIFController () 27 | @property(nonatomic, strong) IBOutlet ORWindowTitleDecorationController *windowDecorationController; 28 | 29 | @property(nonatomic, copy) NSURL *lastUserSelectedDirectory; 30 | @property(nonatomic, copy) ORGIFRightClickMenuMaker *menuMaker; 31 | @end 32 | 33 | @implementation ORGIFController { 34 | NSObject *_currentSource; 35 | NSSet *_starred; 36 | NSString *_gifPath; 37 | 38 | CALayer *_headerLayer; 39 | } 40 | 41 | - (void)getGIFsFromSourceString:(NSString *)string { 42 | BOOL showGiphyLogo = NO; 43 | 44 | if([string rangeOfString:@"/r/"].location != NSNotFound) { 45 | _currentSource = _redditController; 46 | [_redditController setSubreddit:string]; 47 | } 48 | 49 | else if([string rangeOfString:@".tumblr"].location != NSNotFound) { 50 | _currentSource = _tumblrController; 51 | [_tumblrController setTumblrURL:string]; 52 | 53 | } else if([string isEqualToString:@"STARRED"]){ 54 | _currentSource = _starredController; 55 | _starredController.gifController = self; 56 | [_starredController reloadData]; 57 | 58 | } else { 59 | _currentSource = _searchController; 60 | [_searchController setQuery:string]; 61 | [_searchController setAPIKey:[GifsKeys new].giphyAPIKey]; 62 | showGiphyLogo = YES; 63 | } 64 | 65 | [self.windowDecorationController showGiphyLogo:showGiphyLogo]; 66 | [self getNextGIFs]; 67 | [self.collectionView reloadData]; 68 | self.sectionTitle.stringValue = string; 69 | } 70 | 71 | - (void)awakeFromNib { 72 | self.webView.drawsBackground = NO; 73 | self.webView.frame = self.webView.superview.bounds; 74 | 75 | [self.collectionView.superview setPostsBoundsChangedNotifications:YES]; 76 | [[NSNotificationCenter defaultCenter] addObserver:self 77 | selector:@selector(myTableClipBoundsChanged:) 78 | name:NSViewBoundsDidChangeNotification object:self.collectionView.superview]; 79 | 80 | [[NSAppleEventManager sharedAppleEventManager] setEventHandler:self andSelector:@selector(handleURLEvent:withReplyEvent:) forEventClass:kInternetEventClass andEventID:kAEGetURL]; 81 | 82 | [self loadStarred]; 83 | } 84 | 85 | - (void)loadStarred { 86 | NSString *path = [[NSFileManager defaultManager] pathForPrivateFile:@"starred.data"]; 87 | NSSet *data = [NSKeyedUnarchiver unarchiveObjectWithFile:path]; 88 | 89 | if (!data) data = [NSSet set]; 90 | _starred = [data mutableCopy]; 91 | } 92 | 93 | - (void)saveStarred { 94 | NSString *path = [[NSFileManager defaultManager] pathForPrivateFile:@"starred.data"]; 95 | [NSKeyedArchiver archiveRootObject:_starred toFile:path]; 96 | } 97 | 98 | - (void)handleURLEvent:(NSAppleEventDescriptor*)event withReplyEvent:(NSAppleEventDescriptor*)replyEvent 99 | { 100 | NSString *appleURL = [[event paramDescriptorForKeyword:keyDirectObject] stringValue]; 101 | NSString *download = [appleURL substringBetween:@"?dl=" and:@"***thumb"]; 102 | NSString *thumbnail = [appleURL substringBetween:@"***thumb=" and:@"&***source_title"]; 103 | NSString *source = [[appleURL componentsSeparatedByString:@"&***source="] lastObject]; 104 | NSString *sourceTitle = [appleURL substringBetween:@"***source_title=" and:@"&***source="]; 105 | sourceTitle = [sourceTitle stringByRemovingPercentEncoding]; 106 | 107 | GIF *gif = [[GIF alloc] initWithDownloadURL:download thumbnail:thumbnail source:source sourceTitle:sourceTitle]; 108 | gif.dateAdded = [NSDate date]; 109 | 110 | if([_starred containsObject:gif]){ 111 | NSMutableSet *mutableSet = [NSMutableSet setWithSet:_starred]; 112 | [mutableSet removeObject:gif]; 113 | _starred = mutableSet; 114 | } else { 115 | _starred = [_starred setByAddingObject:gif]; 116 | } 117 | 118 | [self saveStarred]; 119 | [_starredController reloadData]; 120 | [_menuController reloadFavourites]; 121 | 122 | } 123 | 124 | - (void)myTableClipBoundsChanged:(NSNotification *)notification 125 | { 126 | NSClipView *clipView = [notification object]; 127 | NSRect newClipBounds = [clipView bounds]; 128 | CGFloat height = [(NSScrollView *)_collectionView.superview.superview contentSize].height; 129 | 130 | if (CGRectGetMinY(newClipBounds) + CGRectGetHeight(newClipBounds) < height + 20) { 131 | [self getNextGIFs]; 132 | } 133 | 134 | } 135 | 136 | - (void)gotNewGIFs 137 | { 138 | NSClipView *clipView = (NSClipView *)[self.collectionView superview]; 139 | 140 | CGFloat clipViewVisibleHeight = CGRectGetHeight(clipView.documentVisibleRect); 141 | CGFloat clipViewDocumentHeight = CGRectGetHeight([clipView.documentView bounds]); 142 | if (clipViewVisibleHeight == clipViewDocumentHeight) { 143 | [self getNextGIFs]; 144 | } 145 | 146 | } 147 | 148 | - (void)getNextGIFs 149 | { 150 | [_currentSource getNextGIFs:^(NSArray *newGIFs, NSError *error) { 151 | [self.collectionView reloadData]; 152 | [self gotNewGIFs]; 153 | 154 | if (!self.currentGIF){ 155 | NSIndexSet *set = [NSIndexSet indexSetWithIndex:0]; 156 | [self.collectionView setSelectionIndexes:set byExtendingSelection:YES]; 157 | } 158 | }]; 159 | } 160 | 161 | -(NSUInteger)numberOfItemsInImageBrowser:(IKImageBrowserView *)aBrowser 162 | { 163 | return _currentSource.numberOfGifs; 164 | } 165 | 166 | -(id)imageBrowser:(IKImageBrowserView *)aBrowser itemAtIndex:(NSUInteger)index 167 | { 168 | return [_currentSource gifAtIndex:index]; 169 | } 170 | 171 | -(void)imageBrowser:(IKImageBrowserView *)aBrowser cellWasRightClickedAtIndex:(NSUInteger)index withEvent:(NSEvent *)event 172 | { 173 | _menuMaker = [[ORGIFRightClickMenuMaker alloc] initWithGIF:self.currentGIF]; 174 | [NSMenu popUpContextMenu:self.menuMaker.menu withEvent:event forView:aBrowser]; 175 | } 176 | 177 | - (void) imageBrowserSelectionDidChange:(IKImageBrowserView *) aBrowser; 178 | { 179 | NSInteger index = aBrowser.selectionIndexes.firstIndex; 180 | 181 | if (index != NSNotFound) { 182 | GIF *gif = [_currentSource gifAtIndex:index]; 183 | _currentGIF = gif; 184 | 185 | NSString *filePath = [[NSBundle mainBundle] pathForResource:@"gif_template" ofType:@"html"]; 186 | NSString *html = [NSString stringWithContentsOfFile:filePath encoding:NSASCIIStringEncoding error:nil]; 187 | NSString *address = gif.downloadURL.absoluteString ?: @""; 188 | html = [html stringByReplacingOccurrencesOfString:@"{{OR_IMAGE_URL}}" withString:address]; 189 | html = [html stringByReplacingOccurrencesOfString:@"{{OR_THUMB_URL}}" withString:[gif.imageRepresentation absoluteString] ?: @""]; 190 | html = [html stringByReplacingOccurrencesOfString:@"{{OR_SOURCE_URL}}" withString:[gif.sourceURL absoluteString] ?: @""]; 191 | html = [html stringByReplacingOccurrencesOfString:@"{{OR_SOURCE_TITLE}}" withString:gif.sourceTitle ?: @""]; 192 | 193 | self.gifTitle.stringValue = gif.sourceTitle; 194 | 195 | if ([_starredController hasGIFWithDownloadAddress:address]) { 196 | html = [html stringByReplacingOccurrencesOfString:@" id='star' " withString:@" id='star' class='active' "]; 197 | } 198 | 199 | if (html) { 200 | NSString *title = [@" " stringByAppendingString:gif.sourceTitle]; 201 | [self.openGIFContextButton setTitle:title]; 202 | [[_webView mainFrame] loadHTMLString:html baseURL:nil]; 203 | } 204 | } 205 | } 206 | // 207 | // - (NSUInteger) numberOfGroupsInImageBrowser:(IKImageBrowserView *) aBrowser 208 | // { 209 | // return 1; 210 | // } 211 | // 212 | // - (NSDictionary *) imageBrowser:(IKImageBrowserView *)aBrowser groupAtIndex:(NSUInteger)index 213 | // { 214 | // CALayer *headerLayer = [CALayer layer]; 215 | // headerLayer.bounds = CGRectMake(0.0, 0.0, 1.0, 70.0); 216 | // headerLayer.backgroundColor = [NSColor whiteColor].CGColor; 217 | // 218 | // NSValue *range = [NSValue valueWithRange:NSMakeRange(0, [self numberOfItemsInImageBrowser:aBrowser])]; 219 | // 220 | // return @{ 221 | // IKImageBrowserGroupStyleKey: @(IKGroupDisclosureStyle), 222 | // IKImageBrowserGroupHeaderLayer: headerLayer, 223 | // IKImageBrowserGroupRangeKey: range 224 | // }; 225 | // } 226 | 227 | - (NSString *)gifFilePath { 228 | return _gifPath; 229 | } 230 | 231 | - (IBAction)openCurrentGIF:(NSButton *)sender 232 | { 233 | [[NSWorkspace sharedWorkspace] openURL:self.currentGIF.sourceURL]; 234 | } 235 | 236 | - (IBAction)togglePopover:(NSButton *)sender 237 | { 238 | if (!self.createSourcePopover.isShown) { 239 | self.createSourcePopover.behavior = NSPopoverBehaviorTransient; 240 | [self.createSourcePopover showRelativeToRect:[sender bounds] ofView:sender preferredEdge:NSMinYEdge]; 241 | 242 | } else { 243 | [self.createSourcePopover close]; 244 | } 245 | } 246 | 247 | #pragma mark - ORImageBrowserViewDelegate 248 | 249 | - (NSURL *)URLForCurrentGIF 250 | { 251 | return [self.currentGIF downloadURL]; 252 | } 253 | 254 | - (NSURL *)URLForCurrentGIFContext 255 | { 256 | return [self.currentGIF sourceURL]; 257 | } 258 | 259 | - (NSString *)sourceTitleForCurrentGIF 260 | { 261 | return self.currentGIF.sourceTitle; 262 | } 263 | 264 | @end 265 | -------------------------------------------------------------------------------- /objc/GIFs/ORGIFRightClickMenuMaker.h: -------------------------------------------------------------------------------- 1 | // 2 | // ORGIFRightClickMenuMaker.h 3 | // GIFs 4 | // 5 | // Created by Orta on 8/20/14. 6 | // Copyright (c) 2014 Orta Therox. All rights reserved. 7 | // 8 | 9 | @interface ORGIFRightClickMenuMaker : NSObject 10 | 11 | - (instancetype)initWithGIF:(GIF *)gif; 12 | @property (readonly, nonatomic, strong) GIF *gif; 13 | 14 | - (NSMenu *)menu; 15 | - (NSArray *)menuItemsWithImageCopyItem:(NSMenuItem *)item; 16 | @end 17 | -------------------------------------------------------------------------------- /objc/GIFs/ORGIFRightClickMenuMaker.m: -------------------------------------------------------------------------------- 1 | // 2 | // ORGIFRightClickMenuMaker.m 3 | // GIFs 4 | // 5 | // Created by Orta on 8/20/14. 6 | // Copyright (c) 2014 Orta Therox. All rights reserved. 7 | // 8 | 9 | #import "ORGIFRightClickMenuMaker.h" 10 | #import "ORGIFActionsController.h" 11 | 12 | @interface ORGIFRightClickMenuMaker() 13 | @property (readonly, nonatomic, copy) NSArray *sharingServices; 14 | 15 | @end 16 | 17 | @implementation ORGIFRightClickMenuMaker 18 | 19 | - (instancetype)initWithGIF:(GIF *)gif 20 | { 21 | self = [super init]; 22 | if (!self) return nil; 23 | 24 | _gif = gif; 25 | 26 | return self; 27 | } 28 | 29 | - (NSMenu *)menu; 30 | { 31 | NSMenu *menu = [[NSMenu alloc] initWithTitle:@"menu"]; 32 | [menu setAutoenablesItems:NO]; 33 | 34 | for (NSMenuItem *item in [self menuItemsWithImageCopyItem:nil]) { 35 | [menu addItem:item]; 36 | } 37 | return menu; 38 | } 39 | 40 | - (NSArray *)menuItemsWithImageCopyItem:(NSMenuItem *)copyItem 41 | { 42 | NSMutableArray *menuItems = [NSMutableArray array]; 43 | 44 | NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:@"Copy GIF URL to Clipboard" action: @selector(copyURL) keyEquivalent:@"c"]; 45 | [item setTarget:self]; 46 | [menuItems addObject:item]; 47 | 48 | item = [[NSMenuItem alloc] initWithTitle:@"Copy Image Markdown" action: @selector(copyMarkdown) keyEquivalent:@"C"]; 49 | [item setTarget:self]; 50 | [menuItems addObject:item]; 51 | 52 | if (copyItem) { 53 | [menuItems addObject:copyItem]; 54 | } 55 | 56 | [menuItems addObject:[NSMenuItem separatorItem]]; 57 | item = [[NSMenuItem alloc] initWithTitle:@"Open GIF in Browser" action:@selector(openInBrowser) keyEquivalent:@"b"]; 58 | item.target = self; 59 | [menuItems addObject:item]; 60 | 61 | if (self.gif.sourceURL) { 62 | item = [[NSMenuItem alloc] initWithTitle:@"Open GIF Context" action:@selector(openContext) keyEquivalent:@"o"]; 63 | item.target = self; 64 | [menuItems addObject:item]; 65 | } 66 | 67 | [menuItems addObject:[NSMenuItem separatorItem]]; 68 | item = [[NSMenuItem alloc] initWithTitle:@"Download GIF" action:@selector(downloadGIF) keyEquivalent:@"s"]; 69 | item.target = self; 70 | [menuItems addObject:item]; 71 | 72 | [menuItems addObject:[NSMenuItem separatorItem]]; 73 | item = [[NSMenuItem alloc] initWithTitle:@"Post to @RandoGIFs" action:@selector(postToRando) keyEquivalent:@"r"]; 74 | item.target = self; 75 | [menuItems addObject:item]; 76 | 77 | item = [[NSMenuItem alloc] initWithTitle:@"Send to @orta" action:@selector(postToOrta) keyEquivalent:@"O"]; 78 | item.target = self; 79 | [menuItems addObject:item]; 80 | 81 | [menuItems addObject:[NSMenuItem separatorItem]]; 82 | 83 | NSArray *sharingServiceIDs = @[NSSharingServiceNamePostOnFacebook, NSSharingServiceNamePostOnTwitter, NSSharingServiceNamePostOnSinaWeibo, NSSharingServiceNamePostOnTencentWeibo, NSSharingServiceNamePostOnLinkedIn, NSSharingServiceNameComposeEmail, NSSharingServiceNameComposeMessage]; 84 | 85 | _sharingServices = [NSSharingService sharingServicesForItems:sharingServiceIDs]; 86 | for (NSSharingService *currentService in self.sharingServices) { 87 | NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:currentService.title action:@selector(share:) keyEquivalent:@""]; 88 | item.image = currentService.image; 89 | item.representedObject = currentService; 90 | item.target = self; 91 | 92 | currentService.delegate = self; 93 | [menuItems addObject:item]; 94 | } 95 | 96 | return [NSArray arrayWithArray:menuItems]; 97 | 98 | } 99 | 100 | - (void)share:(NSMenuItem *)share 101 | { 102 | NSSharingService *service = share.representedObject; 103 | [service performWithItems:@[@"", self.gif.downloadURL]]; 104 | } 105 | 106 | 107 | - (void)postToOrta 108 | { 109 | [ORGIFActionsController tweetOrtaLinkToURL:self.gif.downloadURL]; 110 | } 111 | 112 | - (void)postToRando 113 | { 114 | [ORGIFActionsController tweetOutLinkToURL:self.gif.downloadURL]; 115 | } 116 | 117 | - (void)downloadGIF 118 | { 119 | [ORGIFActionsController downloadGIFWithURL:self.gif.downloadURL completion:nil]; 120 | } 121 | 122 | - (void)copyURL 123 | { 124 | [ORGIFActionsController copyGIFDownloadURLToClipboard:self.gif.downloadURL]; 125 | } 126 | 127 | - (void)copyMarkdown 128 | { 129 | [ORGIFActionsController copyGIFMarkdownToClipboardWithSourceTitle:self.gif.sourceTitle 130 | downloadURL:self.gif.downloadURL]; 131 | } 132 | 133 | - (void)openInBrowser 134 | { 135 | [ORGIFActionsController openGIFDownloadURLInBrowser:self.gif.downloadURL]; 136 | } 137 | 138 | - (void)openContext 139 | { 140 | [ORGIFActionsController openGIFContextURLInBrowser:self.gif.sourceURL]; 141 | } 142 | 143 | @end 144 | -------------------------------------------------------------------------------- /objc/GIFs/ORImageBrowserView.h: -------------------------------------------------------------------------------- 1 | // 2 | // ORImageBrowserView.h 3 | // GIFs 4 | // 5 | // Created by Orta on 8/24/14. 6 | // Copyright (c) 2014 Orta Therox. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @protocol ORImageBrowserViewDelegate 12 | 13 | - (NSURL *)URLForCurrentGIF; 14 | - (NSURL *)URLForCurrentGIFContext; 15 | - (NSString *)sourceTitleForCurrentGIF; 16 | 17 | @end 18 | 19 | @interface ORImageBrowserView : IKImageBrowserView 20 | 21 | @property (nonatomic, weak) IBOutlet id gifDelegate; 22 | 23 | @end 24 | 25 | @interface ORImageBrowserCell : IKImageBrowserCell 26 | 27 | @end -------------------------------------------------------------------------------- /objc/GIFs/ORImageBrowserView.m: -------------------------------------------------------------------------------- 1 | // 2 | // ORImageBrowserView.m 3 | // GIFs 4 | // 5 | // Created by Orta on 8/24/14. 6 | // Copyright (c) 2014 Orta Therox. All rights reserved. 7 | // 8 | 9 | #import "ORImageBrowserView.h" 10 | #import "ORGIFActionsController.h" 11 | 12 | static CGFloat const ORImageBrowserMargin = 3; 13 | 14 | @implementation ORImageBrowserView 15 | 16 | - (IKImageBrowserCell *) newCellForRepresentedItem:(id) cell 17 | { 18 | return [[ORImageBrowserCell alloc] init]; 19 | } 20 | 21 | - (void)copy:(id)sender 22 | { 23 | [ORGIFActionsController copyGIFDownloadURLToClipboard:[self.gifDelegate URLForCurrentGIF]]; 24 | } 25 | 26 | - (void)keyDown:(NSEvent *)theEvent 27 | { 28 | NSURL *gif = [self.gifDelegate URLForCurrentGIF]; 29 | 30 | if (theEvent.modifierFlags & NSCommandKeyMask) { 31 | if ([theEvent.characters isEqualToString:@"b"]) { // Command+b - Open in browser 32 | [ORGIFActionsController openGIFDownloadURLInBrowser:gif]; 33 | } else if ([theEvent.characters isEqualToString:@"o"]) { // Command+o - Open GIF context in browser 34 | [ORGIFActionsController openGIFContextURLInBrowser:gif]; 35 | } else if ([theEvent.characters isEqualToString:@"s"]) { // Command+s - Save GIF to disk 36 | [ORGIFActionsController downloadGIFWithURL:gif completion:nil]; 37 | } else if ([theEvent.characters isEqualToString:@"r"]) { // Command+r - Send Rando GIF 38 | [ORGIFActionsController tweetOutLinkToURL:gif]; 39 | } 40 | 41 | } 42 | 43 | else if ((theEvent.modifierFlags & NSCommandKeyMask) && (theEvent.modifierFlags & NSShiftKeyMask)) { 44 | if ([theEvent.characters isEqualToString:@"c"]) { // Command+Shift+c - Copy GIF markdown to clipboard 45 | [ORGIFActionsController copyGIFMarkdownToClipboardWithSourceTitle:[self.gifDelegate sourceTitleForCurrentGIF] 46 | downloadURL:[self.gifDelegate URLForCurrentGIF]]; 47 | 48 | } else if ([theEvent.characters isEqualToString:@"o"]) { // Command+r - Send Rando GIF 49 | [ORGIFActionsController tweetOrtaLinkToURL:gif]; 50 | } 51 | } 52 | 53 | else { 54 | [super keyDown:theEvent]; 55 | } 56 | } 57 | 58 | @end 59 | 60 | @implementation ORImageBrowserCell 61 | 62 | - (NSRect)imageFrame 63 | { 64 | NSRect imageFrame = [super imageFrame]; 65 | 66 | if (NSIsEmptyRect(imageFrame)) { 67 | return NSZeroRect; 68 | } 69 | 70 | CGFloat aspectRatio = imageFrame.size.width / imageFrame.size.height; 71 | 72 | if (aspectRatio == 1.0f) { 73 | return imageFrame; 74 | } 75 | 76 | NSRect containerFrame = [self imageContainerFrame]; 77 | 78 | if (NSIsEmptyRect(containerFrame)) { 79 | return NSZeroRect; 80 | } 81 | 82 | CGFloat containerAspectRatio = containerFrame.size.width / containerFrame.size.height; 83 | 84 | if (containerAspectRatio > aspectRatio) { 85 | imageFrame.size.height = containerFrame.size.height; 86 | imageFrame.origin.y = containerFrame.origin.y; 87 | imageFrame.size.width = imageFrame.size.height * aspectRatio; 88 | imageFrame.origin.x = containerFrame.origin.x + (containerFrame.size.width - imageFrame.size.width) / 2.0f; 89 | } else { 90 | imageFrame.size.width = containerFrame.size.width; 91 | imageFrame.origin.x = containerFrame.origin.x; 92 | imageFrame.size.height = imageFrame.size.width / aspectRatio; 93 | imageFrame.origin.y = containerFrame.origin.y + (containerFrame.size.height - imageFrame.size.height) / 2.0f; 94 | } 95 | 96 | return imageFrame; 97 | } 98 | 99 | - (NSRect) selectionFrame 100 | { 101 | return NSInsetRect([self frame], -ORImageBrowserMargin, -ORImageBrowserMargin); 102 | } 103 | 104 | - (CALayer *) layerForType:(NSString*) type 105 | { 106 | NSRect frame = [self frame]; 107 | 108 | if(type == IKImageBrowserCellSelectionLayer){ 109 | 110 | CALayer *selectionLayer = [CALayer layer]; 111 | selectionLayer.frame = CGRectMake(0, 0, frame.size.width, frame.size.height); 112 | 113 | NSColor *color = [NSColor selectedMenuItemColor]; 114 | [selectionLayer setBorderColor:color.CGColor]; 115 | 116 | [selectionLayer setBorderWidth:ORImageBrowserMargin]; 117 | [selectionLayer setCornerRadius:0]; 118 | 119 | return selectionLayer; 120 | } 121 | 122 | return [super layerForType:type]; 123 | } 124 | 125 | @end 126 | -------------------------------------------------------------------------------- /objc/GIFs/ORImageView.h: -------------------------------------------------------------------------------- 1 | // 2 | // ORImaageView.h 3 | // GIFs 4 | // 5 | // Created by orta therox on 12/01/2013. 6 | // Copyright (c) 2013 Orta Therox. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ORImageView : IKImageView 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /objc/GIFs/ORImageView.m: -------------------------------------------------------------------------------- 1 | // 2 | // ORImaageView.m 3 | // GIFs 4 | // 5 | // http://blog.pcitron.fr/2010/12/14/play-an-animated-gif-with-an-ikimageview/ 6 | 7 | #import "ORImageView.h" 8 | 9 | @implementation ORImageView 10 | 11 | -(void)awakeFromNib { 12 | // create an overlay for the image (which is used to play animated gifs) 13 | // EDIT: well, don't do that here, due to some initialization orders 14 | // problem, it might gives an error and not create the overlay 15 | // I leave that line here for the records ^^ 16 | //[self setOverlay:[CALayer layer] forType:IKOverlayTypeImage]; 17 | 18 | // NOTE: calling this before anything else seems to fix a lot of 19 | // problems ... maybe it's initializing a few things internally 20 | // on the first call ... 21 | [super setImageWithURL:nil]; 22 | } 23 | 24 | -(BOOL)isGIF:(NSString *)path 25 | { 26 | return YES; 27 | // checks if the path points to a GIF image 28 | NSString * pathExtension = [[path pathExtension] lowercaseString]; 29 | return [pathExtension isEqualToString:@"gif"]; 30 | } 31 | 32 | -(void)setImageWithURL:(NSURL *)url 33 | { 34 | // EDIT: this is where we create the overlay now, but only if it doesn't 35 | // already exists. 36 | // checks if a layer is already set 37 | if ([self overlayForType:IKOverlayTypeImage] == nil) 38 | [self setOverlay:[CALayer layer] forType:IKOverlayTypeImage]; 39 | 40 | // remove the overlay animation 41 | [[self overlayForType:IKOverlayTypeImage] removeAllAnimations]; 42 | 43 | // check if it's a gif 44 | if ([self isGIF:[url path]] == YES) 45 | { 46 | // loads the image 47 | NSImage * image = [[NSImage alloc] initWithContentsOfFile:[url path]]; 48 | 49 | // get the image representations, and iterate through them 50 | NSArray * reps = [image representations]; 51 | for (NSImageRep * rep in reps) { 52 | // find the bitmap representation 53 | if ([rep isKindOfClass:[NSBitmapImageRep class]] == YES) { 54 | // get the bitmap representation 55 | NSBitmapImageRep * bitmapRep = (NSBitmapImageRep *)rep; 56 | 57 | // get the number of frames. If it's 0, it's not an animated gif, do nothing 58 | int numFrame = [[bitmapRep valueForProperty:NSImageFrameCount] intValue]; 59 | if (numFrame == 0) 60 | break; 61 | 62 | // create a value array which will contains the frames of the animation 63 | NSMutableArray * values = [NSMutableArray array]; 64 | 65 | // loop through the frames (animationDuration is the duration of the whole animation) 66 | float animationDuration = 0.0f; 67 | for (int i = 0; i < numFrame; ++i) 68 | { 69 | // set the current frame 70 | [bitmapRep setProperty:NSImageCurrentFrame withValue:[NSNumber numberWithInt:i]]; 71 | 72 | // this part is optional. For some reasons, the NSImage class often loads a GIF with 73 | // frame times of 0, so the GIF plays extremely fast. So, we check the frame duration, and if it's 74 | // less than a threshold value, we set it to a default value of 1/20 second. 75 | if ([[bitmapRep valueForProperty:NSImageCurrentFrameDuration] floatValue] < 0.000001f) 76 | [bitmapRep setProperty:NSImageCurrentFrameDuration withValue:[NSNumber numberWithFloat:1.0f / 20.0f]]; 77 | 78 | // add the CGImageRef to this frame to the value array 79 | [values addObject:(id)[bitmapRep CGImage]]; 80 | 81 | // update the duration of the animation 82 | animationDuration += [[bitmapRep valueForProperty:NSImageCurrentFrameDuration] floatValue]; 83 | } 84 | 85 | // create and setup the animation (this is pretty straightforward) 86 | CAKeyframeAnimation * animation = [CAKeyframeAnimation animationWithKeyPath:@"contents"]; 87 | [animation setValues:values]; 88 | [animation setCalculationMode:@"discrete"]; 89 | [animation setDuration:animationDuration]; 90 | [animation setRepeatCount:HUGE_VAL]; 91 | 92 | // add the animation to the layer 93 | [[self overlayForType:IKOverlayTypeImage] addAnimation:animation forKey:@"contents"]; 94 | 95 | // stops at the first valid representation 96 | break; 97 | } 98 | } 99 | } 100 | 101 | // calls the super setImageWithURL method to handle standard images 102 | [super setImageWithURL:url]; 103 | } 104 | 105 | @end 106 | -------------------------------------------------------------------------------- /objc/GIFs/ORMenuController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ORMenuController.h 3 | // GIFs 4 | // 5 | // Created by orta therox on 12/01/2013. 6 | // Copyright (c) 2013 Orta Therox. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "ORGIFController.h" 11 | #import "ORSimpleSourceListView.h" 12 | 13 | @class ORStarredSourceController; 14 | 15 | @interface ORMenuController : NSObject 16 | 17 | @property (weak) IBOutlet ORGIFController *gifViewController; 18 | @property (weak) IBOutlet ORSimpleSourceListView *menuTableView; 19 | @property (weak) IBOutlet ORStarredSourceController *starredController; 20 | 21 | @property (unsafe_unretained) IBOutlet NSWindow *window; 22 | @property (weak) IBOutlet NSView *windowToolbar; 23 | @property (weak) IBOutlet NSSplitView *mainSplitView; 24 | 25 | @property (weak) IBOutlet NSTextField *searchBar; 26 | 27 | - (IBAction)makeSearchFieldFirstResponder:(id)sender; 28 | - (void)addNewSubreddit:(NSString *)subreddit; 29 | - (void)addNewTumblr:(NSString *)tumblr; 30 | - (void)reloadFavourites; 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /objc/GIFs/ORMenuController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ORMenuController.m 3 | // GIFs 4 | // 5 | // Created by orta therox on 12/01/2013. 6 | // Copyright (c) 2013 Orta Therox. All rights reserved. 7 | // 8 | 9 | #import "ORMenuController.h" 10 | #import 11 | #import "ORStarredSourceController.h" 12 | 13 | NS_ENUM(NSUInteger, ORMenuTitle){ 14 | ORMenuTitleStar, 15 | ORMenuTitleSearch, 16 | ORMenuTitleReddit, 17 | ORMenuTitleTumblr 18 | }; 19 | 20 | @interface ORMenuItem : NSObject 21 | + (id)itemWithName:(NSString *)name address:(NSString *)address; 22 | 23 | @property (copy, nonatomic) NSString *name; 24 | @property (copy, nonatomic) NSString *address; 25 | @end 26 | 27 | @implementation ORMenuItem 28 | 29 | + (id)itemWithName:(NSString *)name address:(NSString *)address { 30 | ORMenuItem *item = [[ORMenuItem alloc] init]; 31 | item.name = name; 32 | item.address = address; 33 | return item; 34 | } 35 | 36 | #define kNameKey @"name" 37 | #define kAddressKey @"address" 38 | 39 | - (void)encodeWithCoder:(NSCoder *)encoder { 40 | [encoder encodeObject:_name forKey:kNameKey]; 41 | [encoder encodeObject:_address forKey:kAddressKey]; 42 | } 43 | 44 | - (id)initWithCoder:(NSCoder *)decoder { 45 | NSString *name = [decoder decodeObjectForKey:kNameKey]; 46 | NSString *address = [decoder decodeObjectForKey:kAddressKey]; 47 | return [self.class itemWithName:name address:address]; 48 | } 49 | 50 | @end 51 | 52 | @implementation ORMenuController { 53 | NSMutableArray *_redditSources; 54 | NSMutableArray *_tumblrSources; 55 | 56 | NSMutableArray *_searches; 57 | } 58 | 59 | - (id) init { 60 | self = [super init]; 61 | if (!self) return nil; 62 | 63 | _searches = [@[] mutableCopy]; 64 | 65 | [self loadReddit]; 66 | [self loadTumblr]; 67 | 68 | return self; 69 | } 70 | 71 | - (BOOL)control:(NSControl *)control textShouldEndEditing:(NSText *)fieldEditor { 72 | NSTextField *searchField = (NSTextField *)control; 73 | [_searches insertObject:searchField.stringValue atIndex:0]; 74 | [searchField setStringValue:@""]; 75 | 76 | [_menuTableView reloadData]; 77 | 78 | [_menuTableView setSelectedIndexPath:[NSIndexPath indexPathForRow:1 inSection:0]]; 79 | return YES; 80 | } 81 | 82 | - (void)reloadFavourites 83 | { 84 | 85 | NSTableRowView *row = [self.menuTableView rowViewAtRow:1 makeIfNecessary:NO]; 86 | NSTableCellView *cell = [row viewAtColumn:0]; 87 | 88 | CABasicAnimation *anime = [CABasicAnimation animationWithKeyPath:@"backgroundColor"]; 89 | anime.fromValue = (id)[cell.layer backgroundColor]; 90 | anime.toValue = (id) [NSColor colorWithCalibratedRed:1.000 green:0.965 blue:0.722 alpha:1.000].CGColor; 91 | anime.duration = 0.15; 92 | anime.autoreverses = YES; 93 | 94 | [cell.layer addAnimation:anime forKey:@"backgroundColor"]; 95 | cell.textField.stringValue = [self titleForStars]; 96 | } 97 | 98 | #pragma mark - 99 | #pragma mark ORSourceListDataSource 100 | 101 | 102 | - (NSImage *)sourceList:(ORSimpleSourceListView *)sourceList imageForHeaderInSection:(NSUInteger)section { 103 | switch (section) { 104 | case ORMenuTitleSearch: 105 | return [NSImage imageNamed:@"SearchWhite"]; 106 | 107 | case ORMenuTitleReddit: 108 | return [NSImage imageNamed:@"RedditWhite"]; 109 | 110 | case ORMenuTitleTumblr: 111 | return [NSImage imageNamed:@"tumblr_t_active"]; 112 | 113 | case ORMenuTitleStar: 114 | return [NSImage imageNamed:@"tick"]; 115 | } 116 | 117 | return nil; 118 | } 119 | 120 | - (NSString *)sourceList:(ORSimpleSourceListView *)sourceList titleOfHeaderForSection:(NSUInteger)section { 121 | switch (section) { 122 | case ORMenuTitleSearch: 123 | return @"Search"; 124 | 125 | case ORMenuTitleReddit: 126 | return @"Reddit"; 127 | 128 | case ORMenuTitleTumblr: 129 | return @"Tumblr"; 130 | 131 | case ORMenuTitleStar: 132 | return @"Starred"; 133 | } 134 | 135 | return @""; 136 | } 137 | 138 | - (ORSourceListItem *)sourceList:(ORSimpleSourceListView *)sourceList sourceListItemForIndexPath:(NSIndexPath *)indexPath { 139 | ORSourceListItem *item = [[ORSourceListItem alloc] init]; 140 | ORMenuItem *menuItem = nil; 141 | item.rightButtonImage = @"close"; 142 | item.rightButtonActiveImage = @"close_active"; 143 | 144 | switch (indexPath.section) { 145 | case ORMenuTitleSearch: 146 | item.title = _searches[indexPath.row]; 147 | item.thumbnail = @"Search"; 148 | item.selectedThumbnail = @"SearchWhite"; 149 | return item; 150 | 151 | case ORMenuTitleReddit: 152 | menuItem = _redditSources[indexPath.row]; 153 | item.title = [menuItem name]; 154 | item.thumbnail = @"Reddit"; 155 | item.selectedThumbnail = @"RedditWhite"; 156 | 157 | return item; 158 | 159 | case ORMenuTitleTumblr: 160 | menuItem = _tumblrSources[indexPath.row]; 161 | item.title = menuItem.name; 162 | item.thumbnail = @"tumblr_t"; 163 | item.selectedThumbnail = @"tumblr_t_active"; 164 | 165 | return item; 166 | 167 | case ORMenuTitleStar: 168 | item.title = [self titleForStars]; 169 | return item; 170 | } 171 | 172 | return nil; 173 | } 174 | 175 | - (NSString *)titleForStars 176 | { 177 | NSInteger count = [self.starredController numberOfGifs]; 178 | switch (count) { 179 | case 0: 180 | return @"No ⭐s"; 181 | case 1: 182 | return @"⭐"; 183 | case 2: 184 | return @"⭐⭐"; 185 | case 3: 186 | return @"⭐⭐⭐"; 187 | case 4: 188 | return @"⭐⭐⭐⭐"; 189 | case 5: 190 | return @"⭐⭐⭐⭐⭐"; 191 | default: 192 | return [NSString stringWithFormat:@"%li ⭐s", (long)count]; 193 | } 194 | } 195 | 196 | - (NSUInteger)sourceList:(ORSimpleSourceListView *)sourceList numberOfRowsInSection:(NSUInteger)section { 197 | switch (section) { 198 | case ORMenuTitleSearch: 199 | return _searches.count; 200 | 201 | case ORMenuTitleReddit: 202 | return _redditSources.count; 203 | 204 | case ORMenuTitleTumblr: 205 | return _tumblrSources.count; 206 | 207 | case ORMenuTitleStar: 208 | return 1; 209 | } 210 | return 0; 211 | } 212 | 213 | - (NSUInteger)numberOfSectionsInSourceList:(ORSimpleSourceListView *)sourceList { 214 | return 4; 215 | } 216 | 217 | #pragma mark - 218 | #pragma mark ORSourceListDelegate 219 | 220 | - (void)sourceList:(ORSimpleSourceListView *)sourceList selectionDidChangeToIndexPath:(NSIndexPath *)indexPath { 221 | NSUInteger index = indexPath.row; 222 | ORMenuItem *item = nil; 223 | 224 | switch (indexPath.section) { 225 | case ORMenuTitleSearch: 226 | [_gifViewController getGIFsFromSourceString:_searches[index]]; 227 | break; 228 | 229 | case ORMenuTitleReddit: 230 | item = _redditSources[index]; 231 | [_gifViewController getGIFsFromSourceString:item.name]; 232 | break; 233 | 234 | case ORMenuTitleTumblr: 235 | item = _tumblrSources[index]; 236 | [_gifViewController getGIFsFromSourceString:item.address]; 237 | break; 238 | 239 | case ORMenuTitleStar: 240 | [_gifViewController getGIFsFromSourceString:@"STARRED"]; 241 | } 242 | } 243 | 244 | - (void)sourceList:(ORSimpleSourceListView *)sourceList didClickOnRightButtonForIndexPath:(NSIndexPath *)indexPath 245 | { 246 | NSUInteger index = indexPath.row; 247 | 248 | switch (indexPath.section) { 249 | case ORMenuTitleSearch: 250 | [_searches removeObjectAtIndex:index]; 251 | break; 252 | 253 | case ORMenuTitleReddit: 254 | [_redditSources removeObjectAtIndex:index]; 255 | [self saveReddit]; 256 | break; 257 | 258 | case ORMenuTitleTumblr: 259 | [_tumblrSources removeObjectAtIndex:index]; 260 | [self saveTumblr]; 261 | break; 262 | 263 | case ORMenuTitleStar: 264 | break; 265 | } 266 | 267 | [sourceList reloadData]; 268 | } 269 | 270 | #pragma mark defaults 271 | 272 | - (NSArray *)defaultRedditSources { 273 | return @[ 274 | [ORMenuItem itemWithName:@"/r/ReactionGIFs" address:@"reactiongifs"], 275 | [ORMenuItem itemWithName:@"/r/GIFs" address:@"gifs"], 276 | [ORMenuItem itemWithName:@"/r/GIF" address:@"gif"], 277 | [ORMenuItem itemWithName:@"/r/aww_gifs" address:@"aww_gifs"], 278 | [ORMenuItem itemWithName:@"/r/Cinemagraphs" address:@"cinemagraphs"], 279 | [ORMenuItem itemWithName:@"/r/chemicalreactiongifs" address:@"chemicalreactiongifs"], 280 | [ORMenuItem itemWithName:@"/r/perfectloops" address:@"perfectloops"], 281 | [ORMenuItem itemWithName:@"/r/highqualitygifs" address:@"highqualitygifs"], 282 | ]; 283 | } 284 | 285 | - (NSArray *)defaultTumblrSources { 286 | return @[ 287 | [ORMenuItem itemWithName:@"What Should We Call Me" address:@"http://whatshouldwecallme.tumblr.com"], 288 | [ORMenuItem itemWithName:@"Animaly GIFs" address:@"http://animalygifs.tumblr.com"], 289 | [ORMenuItem itemWithName:@"GIF Movie" address:@"http://gifmovie.tumblr.com"], 290 | [ORMenuItem itemWithName:@"Reality TV GIFs" address:@"http://realitytvgifs.tumblr.com"], 291 | [ORMenuItem itemWithName:@"Mr GIF" address:@"http://mr-gif.com"], 292 | [ORMenuItem itemWithName:@"Beer Labels In Motion" address:@"http://beerlabelsinmotion.tumblr.com"], 293 | [ORMenuItem itemWithName:@"Bees & Bombs" address:@"http://beesandbombs.tumblr.com"] 294 | ]; 295 | } 296 | 297 | - (void)loadTumblr { 298 | NSString *path = [[NSFileManager defaultManager] pathForPrivateFile:@"tumblr.data"]; 299 | NSArray *data = [NSKeyedUnarchiver unarchiveObjectWithFile:path]; 300 | 301 | if (!data) data = [self defaultTumblrSources]; 302 | _tumblrSources = [data mutableCopy]; 303 | } 304 | 305 | - (void)saveTumblr { 306 | NSString *path = [[NSFileManager defaultManager] pathForPrivateFile:@"tumblr.data"]; 307 | [NSKeyedArchiver archiveRootObject:_tumblrSources toFile:path]; 308 | } 309 | 310 | - (void)addNewTumblr:(NSString *)tumblr { 311 | NSString *name = [tumblr stringByReplacingOccurrencesOfString:@"http://" withString:@""]; 312 | name = [name stringByReplacingOccurrencesOfString:@".tumblr.com" withString:@""]; 313 | 314 | ORMenuItem *item =[ORMenuItem itemWithName:name address:tumblr]; 315 | [_tumblrSources addObject:item]; 316 | [self.menuTableView reloadData]; 317 | [self saveTumblr]; 318 | } 319 | 320 | 321 | - (void)loadReddit { 322 | NSString *path = [[NSFileManager defaultManager] pathForPrivateFile:@"reddit.data"]; 323 | NSArray *data = [NSKeyedUnarchiver unarchiveObjectWithFile:path]; 324 | 325 | if (!data) data = [self defaultRedditSources]; 326 | _redditSources = [data mutableCopy]; 327 | } 328 | 329 | - (void)saveReddit { 330 | NSString *path = [[NSFileManager defaultManager] pathForPrivateFile:@"reddit.data"]; 331 | [NSKeyedArchiver archiveRootObject:_redditSources toFile:path]; 332 | } 333 | 334 | - (void)addNewSubreddit:(NSString *)subreddit { 335 | NSString *address = [subreddit stringByReplacingOccurrencesOfString:@"/r/" withString:@""]; 336 | address = [address stringByReplacingOccurrencesOfString:@"http://reddit.com/" withString:@""]; 337 | address = [address stringByReplacingOccurrencesOfString:@"/" withString:@""]; 338 | 339 | ORMenuItem *item =[ORMenuItem itemWithName:subreddit address:address]; 340 | [_redditSources addObject:item]; 341 | [self.menuTableView reloadData]; 342 | [self saveReddit]; 343 | } 344 | 345 | - (void)makeSearchFieldFirstResponder:(id)sender 346 | { 347 | [self.searchBar becomeFirstResponder]; 348 | } 349 | 350 | @end 351 | -------------------------------------------------------------------------------- /objc/GIFs/ORMiddleGreyView.h: -------------------------------------------------------------------------------- 1 | // 2 | // ORMiddleGreyView.h 3 | // GIFs 4 | // 5 | // Created by Orta on 8/13/14. 6 | // Copyright (c) 2014 Orta Therox. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ORMiddleGreyView : NSView 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /objc/GIFs/ORMiddleGreyView.m: -------------------------------------------------------------------------------- 1 | // 2 | // ORMiddleGreyView.m 3 | // GIFs 4 | // 5 | // Created by Orta on 8/13/14. 6 | // Copyright (c) 2014 Orta Therox. All rights reserved. 7 | // 8 | 9 | #import "ORMiddleGreyView.h" 10 | 11 | @implementation ORMiddleGreyView 12 | 13 | - (void)drawRect:(NSRect)dirtyRect { 14 | [[NSColor colorWithCalibratedRed:0.769 green:0.755 blue:0.756 alpha:1.000] setFill]; 15 | NSRectFill(dirtyRect); 16 | [super drawRect:dirtyRect]; 17 | } 18 | 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /objc/GIFs/ORSearchTextField.h: -------------------------------------------------------------------------------- 1 | // 2 | // ORSearchTextField.h 3 | // GIFs 4 | // 5 | // Created by Orta on 8/31/14. 6 | // Copyright (c) 2014 Orta Therox. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ORSearchTextField : NSTextField 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /objc/GIFs/ORSearchTextField.m: -------------------------------------------------------------------------------- 1 | // 2 | // ORSearchTextField.m 3 | // GIFs 4 | // 5 | // Created by Orta on 8/31/14. 6 | // Copyright (c) 2014 Orta Therox. All rights reserved. 7 | // 8 | 9 | #import "ORSearchTextField.h" 10 | 11 | @implementation ORSearchTextField 12 | 13 | - (void)drawRect:(NSRect)dirtyRect 14 | { 15 | [[NSColor colorWithCalibratedRed:0.150 green:0.140 blue:0.169 alpha:1.000] setFill]; 16 | NSRectFill(dirtyRect); 17 | [super drawRect:dirtyRect]; 18 | } 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /objc/GIFs/ORSimpleSourceListView.h: -------------------------------------------------------------------------------- 1 | // 2 | // ORSimpleSourceListView.h 3 | // GIFs 4 | // 5 | // Created by orta therox on 20/01/2013. 6 | // Copyright (c) 2013 Orta Therox. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class ORSimpleSourceListView, ORSourceListItem; 12 | 13 | @interface NSIndexPath(SourceListExtension) 14 | + (NSIndexPath *)indexPathForRow:(NSInteger)row inSection:(NSInteger)section; 15 | - (NSUInteger)row; 16 | - (NSUInteger)section; 17 | @end 18 | 19 | @interface ORSourceListHeaderView : NSTableCellView 20 | - (id)initWithTitle:(NSString *)title sourceList:(ORSimpleSourceListView *)sourceList; 21 | @end 22 | 23 | @interface ORSourceListItemView : NSTableCellView 24 | - (id)initWithSourceListItem:(ORSourceListItem *)item sourceList:(ORSimpleSourceListView *)sourceList; 25 | @property (assign, nonatomic) BOOL selected; 26 | @property (strong, nonatomic) NSButton *rightImageView; 27 | @end 28 | 29 | @interface ORSourceListRowView : NSTableRowView 30 | @end 31 | 32 | @interface ORSourceListItem : NSObject 33 | @property (strong) NSString *selectedThumbnail; 34 | @property (strong) NSString *thumbnail; 35 | @property (strong) NSString *title; 36 | @property (strong) NSString *rightButtonImage; 37 | @property (strong) NSString *rightButtonActiveImage; 38 | @end 39 | 40 | @protocol ORSourceListDataSource 41 | @required 42 | - (NSUInteger)numberOfSectionsInSourceList:(ORSimpleSourceListView *)sourceList; 43 | - (NSUInteger)sourceList:(ORSimpleSourceListView *)sourceList numberOfRowsInSection:(NSUInteger)section; 44 | 45 | - (NSImage *)sourceList:(ORSimpleSourceListView *)sourceList imageForHeaderInSection:(NSUInteger)section; 46 | - (NSString *)sourceList:(ORSimpleSourceListView *)sourceList titleOfHeaderForSection:(NSUInteger)section; 47 | - (ORSourceListItem *)sourceList:(ORSimpleSourceListView *)sourceList sourceListItemForIndexPath:(NSIndexPath *)indexPath; 48 | @end 49 | 50 | @protocol ORSourceListDelegate 51 | @optional 52 | - (void)sourceList:(ORSimpleSourceListView *)sourceList selectionDidChangeToIndexPath:(NSIndexPath *)indexPath; 53 | - (void)sourceList:(ORSimpleSourceListView *)sourceList didClickOnRightButtonForIndexPath:(NSIndexPath *)indexPath; 54 | @end 55 | 56 | 57 | @interface ORSimpleSourceListView : NSTableView 58 | @property (weak) IBOutlet NSObject *sourceListDelegate; 59 | @property (weak) IBOutlet NSObject *sourceListDataSource; 60 | 61 | @property (strong) NSColor *selectionColor; 62 | @property (strong) NSColor *textColor; 63 | @property (strong) NSColor *highlightedTextColor; 64 | 65 | 66 | - (void)setSelectedIndexPath:(NSIndexPath *)indexPath; 67 | - (void)selectDefaultItem; 68 | @end 69 | -------------------------------------------------------------------------------- /objc/GIFs/ORSimpleSourceListView.m: -------------------------------------------------------------------------------- 1 | // 2 | // ORSimpleSourceListView.m 3 | // GIFs 4 | // 5 | // Created by orta therox on 20/01/2013. 6 | // Copyright (c) 2013 Orta Therox. All rights reserved. 7 | // 8 | 9 | #import "ORSimpleSourceListView.h" 10 | 11 | CGFloat ORCellHeight = 44; 12 | CGFloat ORCellLeftPadding = 12; 13 | 14 | CGFloat ORCellTitleLeftPadding = 42; 15 | 16 | CGFloat ORCellItemLeftPadding = 16; 17 | CGFloat ORCellItemTopPadding = 14; 18 | 19 | CGFloat ORCellImageDimensions = 32; 20 | @class ORSimpleTextField; 21 | 22 | @implementation NSIndexPath(SourceListExtension) 23 | + (NSIndexPath *)indexPathForRow:(NSInteger)row inSection:(NSInteger)section { 24 | NSUInteger indexArr[] = { row, section }; 25 | return [NSIndexPath indexPathWithIndexes:indexArr length:2]; 26 | } 27 | - (NSUInteger)row { return [self indexAtPosition:0]; } 28 | - (NSUInteger)section { return [self indexAtPosition:1]; } 29 | @end 30 | 31 | 32 | 33 | @interface ORSimpleTextField : NSTextField 34 | @end 35 | 36 | @implementation ORSimpleTextField 37 | 38 | - (BOOL)allowsVibrancy 39 | { 40 | return NO; 41 | } 42 | 43 | @end 44 | 45 | @implementation ORSourceListItem 46 | 47 | @end 48 | 49 | @implementation ORSimpleSourceListView { 50 | // Items stores both titles and items 51 | NSArray *_items; 52 | NSUInteger _currentSelectionIndex; 53 | } 54 | 55 | - (id)initWithFrame:(NSRect)frame { 56 | self = [super initWithFrame:frame]; 57 | if (self) { 58 | [self __setup]; 59 | } 60 | return self; 61 | } 62 | 63 | - (id)initWithCoder:(NSCoder *)aDecoder { 64 | self = [super initWithCoder:aDecoder]; 65 | if (self) { 66 | [self __setup]; 67 | } 68 | return self; 69 | } 70 | 71 | - (void)__setup { 72 | self.delegate = self; 73 | self.dataSource = self; 74 | self.focusRingType = NSFocusRingTypeNone; 75 | self.headerView = nil; 76 | self.backgroundColor = [NSColor clearColor]; 77 | self.enclosingScrollView.drawsBackground = NO; 78 | self.intercellSpacing = CGSizeMake(0, 0); 79 | self.allowsMultipleSelection = NO; 80 | self.allowsEmptySelection = NO; 81 | self.enclosingScrollView.scrollerStyle = NSScrollerStyleOverlay; 82 | 83 | _currentSelectionIndex = NSNotFound; 84 | } 85 | 86 | - (void)awakeFromNib { 87 | [self reloadData]; 88 | } 89 | 90 | - (void)reloadData { 91 | if(_sourceListDataSource){ 92 | NSUInteger numberOfSections = [_sourceListDataSource numberOfSectionsInSourceList:self]; 93 | if(!numberOfSections) return; 94 | 95 | NSMutableArray *mutableItems = [NSMutableArray array]; 96 | for (int i = 0; i < numberOfSections; i++) { 97 | [mutableItems addObject:[_sourceListDataSource sourceList:self titleOfHeaderForSection:i]]; 98 | NSUInteger numberOfRows = [_sourceListDataSource sourceList:self numberOfRowsInSection:i]; 99 | 100 | for (int j = 0; j < numberOfRows; j++) { 101 | NSIndexPath *currentIndexPath = [NSIndexPath indexPathForRow:j inSection:i]; 102 | [mutableItems addObject:[_sourceListDataSource sourceList:self sourceListItemForIndexPath:currentIndexPath]]; 103 | } 104 | } 105 | _items = [NSArray arrayWithArray:mutableItems]; 106 | } 107 | 108 | [super reloadData]; 109 | } 110 | 111 | - (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView { 112 | return _items.count; 113 | } 114 | 115 | - (CGFloat)tableView:(NSTableView *)tableView heightOfRow:(NSInteger)row { 116 | return ORCellHeight; 117 | } 118 | 119 | - (NSView *)tableView:(NSTableView *)tableView 120 | viewForTableColumn:(NSTableColumn *)tableColumn 121 | row:(NSInteger)row { 122 | id item =_items[row]; 123 | NSTableCellView *result; 124 | if ([item isMemberOfClass:[ORSourceListItem class]]){ 125 | ORSourceListItem *sourceListItem = (ORSourceListItem *)item; 126 | result = [[ORSourceListItemView alloc] initWithSourceListItem:sourceListItem sourceList:self]; 127 | 128 | if (row == _currentSelectionIndex) { 129 | [(ORSourceListItemView *)result setSelected:YES]; 130 | } 131 | 132 | } else { 133 | NSString *headerString = (NSString *)item; 134 | result = [[ORSourceListHeaderView alloc] initWithTitle:headerString sourceList:self]; 135 | 136 | NSIndexPath *indexPath = [self rowToIndexPath:row]; 137 | result.imageView.image = [self.sourceListDataSource sourceList:self imageForHeaderInSection:indexPath.section]; 138 | } 139 | 140 | return result; 141 | } 142 | 143 | - (NSTableRowView *)tableView:(NSTableView *)tableView rowViewForRow:(NSInteger)row 144 | { 145 | return [[ORSourceListRowView alloc] init]; 146 | } 147 | 148 | - (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(NSInteger)row { 149 | id item =_items[row]; 150 | return [item isKindOfClass:[ORSourceListItem class]]; 151 | } 152 | 153 | - (void)selectDefaultItem 154 | { 155 | NSIndexSet *set = [NSIndexSet indexSetWithIndex:4]; 156 | [self selectRowIndexes:set byExtendingSelection:NO]; 157 | } 158 | 159 | - (void)setSelectedIndexPath:(NSIndexPath *)indexPath { 160 | NSInteger index = [self indexPathToRow:indexPath]; 161 | if (index != NSNotFound) { 162 | NSIndexSet *set = [[NSIndexSet alloc] initWithIndex:index]; 163 | [self selectRowIndexes:set byExtendingSelection:NO]; 164 | } 165 | } 166 | 167 | - (void)selectRowIndexes:(NSIndexSet *)indexes byExtendingSelection:(BOOL)extend { 168 | NSUInteger newIndex = [indexes firstIndex]; 169 | if (newIndex != NSNotFound) { 170 | [self _changeSelectionToRow:newIndex]; 171 | [super selectRowIndexes:indexes byExtendingSelection:extend]; 172 | 173 | id item = _items[newIndex]; 174 | if ([item isMemberOfClass:[ORSourceListItem class]]) { 175 | NSIndexPath *path = [self rowToIndexPath:newIndex]; 176 | [_sourceListDelegate sourceList:self selectionDidChangeToIndexPath:path]; 177 | } 178 | } 179 | } 180 | 181 | - (void)_changeSelectionToRow:(NSInteger)row { 182 | if (_currentSelectionIndex != NSNotFound) { 183 | [self _visuallySelectRowAtIndex:_currentSelectionIndex toState:NO]; 184 | } 185 | _currentSelectionIndex = row; 186 | [self _visuallySelectRowAtIndex:row toState:YES]; 187 | } 188 | 189 | - (void)_visuallySelectRowAtIndex:(NSUInteger)index toState:(BOOL)state { 190 | ORSourceListRowView *row = [self rowViewAtRow:index makeIfNecessary:NO]; 191 | row.selected = state; 192 | } 193 | 194 | - (NSUInteger)indexPathToRow:(NSIndexPath *)path { 195 | NSInteger section = -1; 196 | NSInteger currentRow = -1; 197 | BOOL foundSection = NO; 198 | for (int i = 0; i < _items.count; i++) { 199 | if (![_items[i] isKindOfClass:[ORSourceListItem class]]) { 200 | section++; 201 | if (section == path.section) { 202 | foundSection = YES; 203 | } 204 | } 205 | else if (foundSection) { 206 | currentRow++; 207 | if (currentRow == path.row) { 208 | return i; 209 | } 210 | } 211 | } 212 | return NSNotFound; 213 | } 214 | 215 | - (NSIndexPath *)rowToIndexPath:(NSUInteger)row { 216 | NSInteger indexRow = -1; 217 | NSInteger indexSection = -1; 218 | for (int i = 0; i < (row + 1); i++) { 219 | if ([_items[i] isKindOfClass:[ORSourceListItem class]]) { 220 | indexRow++; 221 | } else { 222 | indexSection++; 223 | indexRow = -1; 224 | } 225 | } 226 | return [NSIndexPath indexPathForRow:indexRow inSection:indexSection]; 227 | } 228 | 229 | - (ORSimpleTextField *)textFieldForItems { 230 | ORSimpleTextField *titleLabel = [[ORSimpleTextField alloc] init]; 231 | [titleLabel setBezeled:NO]; 232 | [titleLabel setDrawsBackground:NO]; 233 | [titleLabel setEditable:NO]; 234 | [titleLabel setSelectable:NO]; 235 | [[titleLabel cell] setBackgroundStyle:NSBackgroundStyleDark]; 236 | titleLabel.font = [NSFont systemFontOfSize:13]; 237 | titleLabel.textColor = self.textColor; 238 | return titleLabel; 239 | } 240 | 241 | - (NSTextField *)textFieldForHeaders { 242 | NSTextField *headerTextField = [self textFieldForItems]; 243 | headerTextField.font = [NSFont boldSystemFontOfSize:13]; 244 | headerTextField.textColor = [NSColor colorWithCalibratedRed:0.455 green:0.442 blue:0.459 alpha:1.000]; 245 | return headerTextField; 246 | } 247 | 248 | 249 | - (void)tappedOnRightButton:(NSButton *)button { 250 | if ([_sourceListDelegate respondsToSelector:@selector(sourceList:didClickOnRightButtonForIndexPath:)]) { 251 | NSIndexPath *path = [self rowToIndexPath:button.tag]; 252 | [_sourceListDelegate sourceList:self didClickOnRightButtonForIndexPath:path]; 253 | } 254 | } 255 | 256 | @end 257 | 258 | @implementation ORSourceListHeaderView 259 | 260 | - (id)initWithTitle:(NSString *)title sourceList:(ORSimpleSourceListView *)sourceList { 261 | self = [super init]; 262 | if(!self)return nil; 263 | 264 | NSTextField *headerTextField = sourceList.textFieldForHeaders; 265 | self.textField = headerTextField; 266 | [self addSubview:headerTextField]; 267 | 268 | NSImageView *itemImage = [[NSImageView alloc] init]; 269 | self.imageView = itemImage; 270 | [self addSubview:itemImage]; 271 | 272 | self.textField.stringValue = [title uppercaseString]; 273 | return self; 274 | } 275 | 276 | - (void)setFrame:(NSRect)frameRect { 277 | [super setFrame:frameRect]; 278 | 279 | self.textField.frame = CGRectMake(ORCellTitleLeftPadding, -ORCellItemTopPadding, CGRectGetWidth(frameRect) - ORCellItemLeftPadding, ORCellHeight); 280 | self.imageView.frame = CGRectMake(ORCellLeftPadding, CGRectGetHeight(frameRect)/2 - ORCellImageDimensions/2, ORCellImageDimensions, ORCellImageDimensions); 281 | } 282 | 283 | - (void)drawRect:(NSRect)dirtyRect { 284 | if (self.allowsVibrancy) { 285 | [[NSColor colorWithCalibratedRed:0.162 green:0.137 blue:0.160 alpha:0.300] set]; 286 | } else { 287 | [[NSColor colorWithCalibratedRed:0.264 green:0.229 blue:0.257 alpha:1.000] set]; 288 | } 289 | 290 | NSRectFill(dirtyRect); 291 | } 292 | 293 | @end 294 | 295 | @implementation ORSourceListItemView { 296 | 297 | NSString *_thumbnail; 298 | NSString *_selectedThumbnail; 299 | NSString *_rightImageName; 300 | NSString *_rightImageActiveName; 301 | ORSimpleSourceListView *_sourceList; 302 | } 303 | 304 | - (id)initWithSourceListItem:(ORSourceListItem *)item sourceList:(ORSimpleSourceListView *)sourceList { 305 | self = [super init]; 306 | if (!self)return nil; 307 | 308 | _thumbnail = item.thumbnail.copy; 309 | _selectedThumbnail = item.selectedThumbnail.copy; 310 | _rightImageName = item.rightButtonImage.copy; 311 | _rightImageActiveName = item.rightButtonActiveImage.copy; 312 | _sourceList = sourceList; 313 | 314 | ORSimpleTextField *titleTextField = [sourceList textFieldForItems]; 315 | titleTextField.lineBreakMode = NSLineBreakByTruncatingTail; 316 | self.textField = titleTextField; 317 | [self addSubview:titleTextField]; 318 | 319 | self.textField.stringValue = item. title; 320 | self.textField.drawsBackground = NO; 321 | 322 | return self; 323 | } 324 | 325 | - (void)setFrame:(NSRect)frameRect { 326 | [super setFrame:frameRect]; 327 | 328 | self.textField.frame = CGRectMake(ORCellItemLeftPadding, -ORCellItemTopPadding, CGRectGetWidth(frameRect) - ORCellItemLeftPadding, ORCellHeight); 329 | self.imageView.frame = CGRectMake(0, CGRectGetHeight(frameRect)/2 - ORCellImageDimensions/2, ORCellImageDimensions, ORCellImageDimensions); 330 | } 331 | 332 | @end 333 | 334 | @implementation ORSourceListRowView 335 | 336 | - (void)setSelected:(BOOL)selected 337 | { 338 | [super setSelected:selected]; 339 | 340 | NSColor *color = selected ? [NSColor selectedMenuItemColor] : [ NSColor clearColor]; 341 | 342 | for (NSView *view in self.subviews) { 343 | view.layer.backgroundColor = color.CGColor; 344 | } 345 | self.backgroundColor = color; 346 | 347 | } 348 | 349 | @end -------------------------------------------------------------------------------- /objc/GIFs/ORSourcesController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ORSourcesController.h 3 | // GIFs 4 | // 5 | // Created by Orta on 14/11/2013. 6 | // Copyright (c) 2013 Orta Therox. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "ORMenuController.h" 11 | 12 | @interface ORSourcesController : NSObject 13 | 14 | @property (nonatomic, weak) IBOutlet ORMenuController *menuController; 15 | @property (nonatomic, weak) IBOutlet NSPopover *sourcePopover; 16 | 17 | @property (weak) IBOutlet NSTextField *tumblrURLTextField; 18 | @property (weak) IBOutlet NSButton *tumblrSaveButton; 19 | 20 | - (IBAction)tumblrTextfieldChanged:(NSTextField *)sender; 21 | - (IBAction)tumblrSavedTapped:(id)sender; 22 | 23 | 24 | @property (weak) IBOutlet NSTextField *redditTextField; 25 | @property (weak) IBOutlet NSButton *redditSaveButton; 26 | 27 | - (IBAction)redditTextFieldChanged:(NSTextField *)sender; 28 | 29 | - (IBAction)redditSaveTapped:(id)sender; 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /objc/GIFs/ORSourcesController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ORSourcesController.m 3 | // GIFs 4 | // 5 | // Created by Orta on 14/11/2013. 6 | // Copyright (c) 2013 Orta Therox. All rights reserved. 7 | // 8 | 9 | #import "ORSourcesController.h" 10 | #import 11 | 12 | @implementation ORSourcesController 13 | 14 | - (IBAction)tumblrTextfieldChanged:(NSTextField *)sender { 15 | 16 | NSString *path = [NSString stringWithFormat:@"%@/api/read/json", sender.stringValue]; 17 | NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:path]]; 18 | 19 | AFHTTPRequestOperation *requestOp = [[AFHTTPRequestOperation alloc] initWithRequest:request]; 20 | [requestOp setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) { 21 | self.tumblrSaveButton.image = [NSImage imageNamed:@"tick_active"]; 22 | 23 | } failure:^(AFHTTPRequestOperation *operation, NSError *error) { 24 | self.tumblrSaveButton.image = [NSImage imageNamed:@"tick"]; 25 | 26 | }]; 27 | [requestOp start]; 28 | } 29 | 30 | - (IBAction)tumblrSavedTapped:(id)sender { 31 | [self.menuController addNewTumblr:self.tumblrURLTextField.stringValue]; 32 | [self.sourcePopover performClose:self]; 33 | } 34 | 35 | - (IBAction)redditTextFieldChanged:(NSTextField *)sender 36 | { 37 | NSString *path = [NSString stringWithFormat:@"http://www.reddit.com%@.json", sender.stringValue]; 38 | NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:path]]; 39 | AFHTTPRequestOperation *requestOp = [[AFHTTPRequestOperation alloc] initWithRequest:request]; 40 | [requestOp setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) { 41 | self.redditSaveButton.image = [NSImage imageNamed:@"tick_active"]; 42 | 43 | } failure:^(AFHTTPRequestOperation *operation, NSError *error) { 44 | self.redditSaveButton.image = [NSImage imageNamed:@"tick"]; 45 | }]; 46 | [requestOp start]; 47 | } 48 | 49 | - (IBAction)redditSaveTapped:(id)sender 50 | { 51 | [self.menuController addNewSubreddit:self.redditTextField.stringValue]; 52 | [self.sourcePopover performClose:self]; 53 | } 54 | 55 | @end 56 | -------------------------------------------------------------------------------- /objc/GIFs/ORStarredSourceController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ORStarredSourceController.h 3 | // GIFs 4 | // 5 | // Created by Orta on 21/11/2013. 6 | // Copyright (c) 2013 Orta Therox. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "ORGIFController.h" 11 | 12 | @class GIF; 13 | 14 | @interface ORStarredSourceController : NSObject 15 | 16 | @property (weak) IBOutlet ORGIFController *gifController; 17 | 18 | - (void)reloadData; 19 | - (BOOL)hasGIFWithDownloadAddress:(NSString *)address; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /objc/GIFs/ORStarredSourceController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ORStarredSourceController.m 3 | // GIFs 4 | // 5 | // Created by Orta on 21/11/2013. 6 | // Copyright (c) 2013 Orta Therox. All rights reserved. 7 | // 8 | 9 | #import "ORStarredSourceController.h" 10 | #import 11 | 12 | 13 | @implementation ORStarredSourceController { 14 | NSArray *_starred; 15 | } 16 | 17 | - (void)awakeFromNib { 18 | [self reloadData]; 19 | } 20 | 21 | - (void)reloadData 22 | { 23 | NSString *path = [[NSFileManager defaultManager] pathForPrivateFile:@"starred.data"]; 24 | NSSet *data = [NSKeyedUnarchiver unarchiveObjectWithFile:path]; 25 | if (data) { 26 | NSSortDescriptor *descriptor = [NSSortDescriptor sortDescriptorWithKey:@"dateAdded" ascending:NO]; 27 | _starred = [data sortedArrayUsingDescriptors:@[descriptor]]; 28 | } 29 | } 30 | 31 | - (void)getNextGIFs:(void (^)(NSArray *, NSError *))completion { 32 | 33 | } 34 | 35 | 36 | - (NSInteger)numberOfGifs { 37 | return _starred.count; 38 | } 39 | 40 | - (GIF *)gifAtIndex:(NSInteger)index { 41 | return _starred[index]; 42 | } 43 | 44 | - (BOOL)hasGIFWithDownloadAddress:(NSString *)address 45 | { 46 | for (GIF *gif in _starred) { 47 | if ([gif.imageUID isEqualToString:address]) { 48 | return YES; 49 | } 50 | } 51 | return NO; 52 | } 53 | 54 | @end 55 | -------------------------------------------------------------------------------- /objc/GIFs/ORTintedVisualEffectView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ORTintedVisualEffectView.swift 3 | // GIFs 4 | // 5 | // Created by Orta on 8/5/14. 6 | // Copyright (c) 2014 Orta Therox. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class ORTintedView: NSView { 12 | 13 | override func drawRect(dirtyRect: NSRect) { 14 | super.drawRect(dirtyRect) 15 | NSColor(calibratedRed: 0.162, green: 0.137, blue: 0.160, alpha: 0.75).set(); 16 | NSRectFill(dirtyRect); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /objc/GIFs/ORTumblrController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ORTumblrController.h 3 | // GIFs 4 | // 5 | // Created by orta therox on 21/01/2013. 6 | // Copyright (c) 2013 Orta Therox. All rights reserved. 7 | // 8 | 9 | #import "ORGIFController.h" 10 | 11 | @interface ORTumblrController : NSObject 12 | - (void)setTumblrURL:(NSString *)tumblrURL; 13 | @end 14 | -------------------------------------------------------------------------------- /objc/GIFs/ORTumblrController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ORTumblrController.m 3 | // GIFs 4 | // 5 | // Created by orta therox on 21/01/2013. 6 | // Copyright (c) 2013 Orta Therox. All rights reserved. 7 | // 8 | 9 | #import "ORTumblrController.h" 10 | #import "AFNetworking.h" 11 | #import "ORAppDelegate.h" 12 | #import 13 | 14 | @interface TumblrGIF : GIF 15 | @property (copy, nonatomic, readwrite) NSString *representedURL; 16 | @property (copy, nonatomic, readwrite) NSString *tumblrID; 17 | 18 | @property (assign, nonatomic, readwrite) NSInteger suffixIndex; 19 | @property (assign, nonatomic, readwrite) BOOL isTumblr; 20 | @end 21 | 22 | @implementation TumblrGIF 23 | 24 | + (NSArray *)suffixes 25 | { 26 | return @[@"75sq", @"100", @"250", @"300", @"400", @"500"]; 27 | } 28 | 29 | - (void)setRepresentedURL:(NSString *)representedURL 30 | { 31 | self.isTumblr = NO; 32 | 33 | for (NSString *size in self.class.suffixes ) { 34 | NSString *remove = [NSString stringWithFormat:@"_%@.gif", size]; 35 | 36 | if ([representedURL containsString:remove]) { 37 | self.suffixIndex = [self.class.suffixes indexOfObject:size]; 38 | self.isTumblr = YES; 39 | } 40 | representedURL = [representedURL stringByReplacingOccurrencesOfString:remove withString:@""]; 41 | } 42 | 43 | if (self.isTumblr) { 44 | self.tumblrID = [[representedURL componentsSeparatedByString:@"tumblr.com"] lastObject]; 45 | } 46 | _representedURL = representedURL; 47 | } 48 | 49 | - (id) imageRepresentation 50 | { 51 | return self.downloadURL; 52 | } 53 | 54 | - (NSURL *)downloadURL 55 | { 56 | if (!self.representedURL) return nil; 57 | 58 | if (self.isTumblr) { 59 | NSString *fullURL = [NSString stringWithFormat:@"%@_%@.gif",self.representedURL, self.class.suffixes[self.suffixIndex]]; 60 | return [NSURL URLWithString:fullURL]; 61 | } else { 62 | return [super downloadURL]; 63 | } 64 | } 65 | 66 | - (NSUInteger)hash 67 | { 68 | if (self.isTumblr) { 69 | return self.tumblrID.hash; 70 | } else { 71 | return self.representedURL.hash; 72 | } 73 | } 74 | 75 | - (BOOL)isEqual:(id)object 76 | { 77 | if ([object isKindOfClass:self.class]) { 78 | if (self.isTumblr) { 79 | return [self.tumblrID isEqual:[object tumblrID]]; 80 | } else { 81 | return [self.representedURL isEqual:[object representedURL]]; 82 | } 83 | 84 | } 85 | return [super isEqual:object]; 86 | } 87 | 88 | @end 89 | 90 | @implementation ORTumblrController { 91 | NSString *_url; 92 | NSArray *_gifs; 93 | 94 | NSInteger _offset; 95 | BOOL _downloading; 96 | } 97 | 98 | - (void)setTumblrURL:(NSString *)tumblrURL { 99 | 100 | _url = tumblrURL; 101 | _gifs = @[]; 102 | _offset = 0; 103 | _downloading = NO; 104 | } 105 | 106 | - (void)getNextGIFs:(void (^)(NSArray *newGIFs, NSError *error))completion; 107 | { 108 | if (_downloading) return; 109 | 110 | // http://whatshouldwecallme.tumblr.com/api/read/json 111 | 112 | NSString *address = [_url stringByAppendingFormat:@"/api/read/json?start=%@", @(_offset)]; 113 | 114 | AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager]; 115 | AFHTTPRequestOperation *op = [manager GET:address parameters:nil success:^(AFHTTPRequestOperation *operation, id JSON) { 116 | 117 | [ORAppDelegate setNetworkActivity:NO]; 118 | _offset += 25; 119 | 120 | NSError *error = nil; 121 | NSString *string = [operation.responseString stringByReplacingOccurrencesOfString:@"\\/" withString:@"/"]; 122 | NSDataDetector *linkDetector = [NSDataDetector dataDetectorWithTypes:NSTextCheckingTypeLink error:&error]; 123 | NSMutableSet *newGIFs = [NSMutableSet set]; 124 | 125 | [linkDetector enumerateMatchesInString:string 126 | options:0 127 | range:NSMakeRange(0, [string length]) 128 | usingBlock:^(NSTextCheckingResult *match, NSMatchingFlags flags, BOOL *stop){ 129 | if ([match resultType] == NSTextCheckingTypeLink) { 130 | NSURL *url = [match URL]; 131 | 132 | if ([url.absoluteString rangeOfString:@".gif"].location != NSNotFound) { 133 | NSString *address = [url.absoluteString stringByReplacingOccurrencesOfString:@"%5C" withString:@""]; 134 | TumblrGIF *gif = [[TumblrGIF alloc] initWithDownloadURL:address thumbnail:address source:_url sourceTitle:_url ]; 135 | gif.representedURL = address; 136 | if (gif) { 137 | if ([newGIFs containsObject:gif]) { 138 | 139 | // Set the suffix index to be the highest of the two 140 | 141 | for (TumblrGIF *searchGIF in newGIFs) { 142 | if ([searchGIF.representedURL isEqualToString:gif.representedURL]) { 143 | searchGIF.suffixIndex = MAX(searchGIF.suffixIndex, gif.suffixIndex); 144 | } 145 | } 146 | } else { 147 | [newGIFs addObject:gif]; 148 | } 149 | 150 | } 151 | } 152 | } 153 | }]; 154 | 155 | _gifs = [_gifs arrayByAddingObjectsFromArray:newGIFs.allObjects]; 156 | _downloading = NO; 157 | if (completion) completion(newGIFs.allObjects, nil); 158 | 159 | } failure:^(AFHTTPRequestOperation *operation, NSError *error) { 160 | if (completion) completion(nil, error); 161 | }]; 162 | op.responseSerializer = [[AFHTTPResponseSerializer alloc] init]; 163 | 164 | _downloading = YES; 165 | [ORAppDelegate setNetworkActivity:YES]; 166 | } 167 | 168 | - (NSInteger)numberOfGifs { 169 | return _gifs.count; 170 | } 171 | 172 | - (GIF *)gifAtIndex:(NSInteger)index { 173 | return _gifs[index]; 174 | } 175 | 176 | @end 177 | -------------------------------------------------------------------------------- /objc/GIFs/ORWebViewDelegateStuff.h: -------------------------------------------------------------------------------- 1 | // 2 | // ORWebViewDelegateStuff.h 3 | // GIFs 4 | // 5 | // Created by orta therox on 12/01/2013. 6 | // Copyright (c) 2013 Orta Therox. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @class ORGIFController; 13 | @interface ORWebViewDelegateStuff : NSObject 14 | @property (weak) IBOutlet WebView *webView; 15 | @property (weak) IBOutlet ORGIFController *gifController; 16 | @end 17 | -------------------------------------------------------------------------------- /objc/GIFs/ORWebViewDelegateStuff.m: -------------------------------------------------------------------------------- 1 | // 2 | // ORWebViewDelegateStuff.m 3 | // GIFs 4 | // 5 | // Created by orta therox on 12/01/2013. 6 | // Copyright (c) 2013 Orta Therox. All rights reserved. 7 | // 8 | 9 | #import "ORWebViewDelegateStuff.h" 10 | #import "ORGIFRightClickMenuMaker.h" 11 | #import "ORGIFController.h" 12 | 13 | @implementation ORWebViewDelegateStuff { 14 | NSURL *currentAddress; 15 | ORGIFRightClickMenuMaker *menuMaker; 16 | } 17 | 18 | - (void)awakeFromNib { 19 | _webView.UIDelegate = self; 20 | } 21 | 22 | - (NSArray *)webView:(WebView *)sender contextMenuItemsForElement:(NSDictionary *)element defaultMenuItems:(NSArray *)defaultMenuItems { 23 | 24 | NSMenuItem *copy = nil; 25 | for (NSMenuItem *item in defaultMenuItems) { 26 | // I want Copy Image, but I can't look at title due to localisation stuff 27 | if (item.tag == 6) { 28 | copy = item; 29 | } 30 | } 31 | 32 | GIF *gif = self.gifController.currentGIF; 33 | if (gif) { 34 | menuMaker = [[ORGIFRightClickMenuMaker alloc] initWithGIF:gif]; 35 | return [menuMaker menuItemsWithImageCopyItem:copy]; 36 | } 37 | return @[]; 38 | } 39 | 40 | 41 | @end 42 | -------------------------------------------------------------------------------- /objc/GIFs/ORWindowDraggableVisualEffectsView.h: -------------------------------------------------------------------------------- 1 | // 2 | // ORWindowDraggableVisualEffectsView.h 3 | // GIFs 4 | // 5 | // Created by Orta on 8/20/14. 6 | // Copyright (c) 2014 Orta Therox. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ORWindowDraggableVisualEffectsView : NSVisualEffectView 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /objc/GIFs/ORWindowDraggableVisualEffectsView.m: -------------------------------------------------------------------------------- 1 | // 2 | // ORWindowDraggableVisualEffectsView.m 3 | // GIFs 4 | // 5 | // Created by Orta on 8/20/14. 6 | // Copyright (c) 2014 Orta Therox. All rights reserved. 7 | // 8 | 9 | #import "ORWindowDraggableVisualEffectsView.h" 10 | 11 | @interface ORWindowDraggableVisualEffectsView() 12 | @property (assign) NSPoint initialLocation; 13 | @end 14 | 15 | @implementation ORWindowDraggableVisualEffectsView 16 | 17 | - (BOOL)acceptsFirstMouse:(NSEvent *)theEvent 18 | { 19 | return YES; 20 | } 21 | 22 | - (void)mouseDown:(NSEvent *)theEvent 23 | { 24 | self.initialLocation = [theEvent locationInWindow]; 25 | } 26 | 27 | - (void)mouseDragged:(NSEvent *)theEvent 28 | { 29 | 30 | NSRect screenVisibleFrame = [self.window.screen visibleFrame]; 31 | NSRect windowFrame = [self.window frame]; 32 | NSPoint newOrigin = windowFrame.origin; 33 | 34 | // Get the mouse location in window coordinates. 35 | NSPoint currentLocation = [theEvent locationInWindow]; 36 | // Update the origin with the difference between the new mouse location and the old mouse location. 37 | newOrigin.x += (currentLocation.x - self.initialLocation.x); 38 | newOrigin.y += (currentLocation.y - self.initialLocation.y); 39 | 40 | // Don't let window get dragged up under the menu bar 41 | if ((newOrigin.y + windowFrame.size.height) > (screenVisibleFrame.origin.y + screenVisibleFrame.size.height)) { 42 | newOrigin.y = screenVisibleFrame.origin.y + (screenVisibleFrame.size.height - windowFrame.size.height); 43 | } 44 | 45 | // Move the window to the new location 46 | [self.window setFrameOrigin:newOrigin]; 47 | } 48 | 49 | 50 | @end 51 | -------------------------------------------------------------------------------- /objc/GIFs/ORWindowTitleDecorationController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ORWindowTitleDecorationController.swift 3 | // GIFs 4 | // 5 | // Created by Orta on 8/7/14. 6 | // Copyright (c) 2014 Orta Therox. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import AppKit 11 | 12 | @objc public class ORWindowTitleDecorationController: NSObject, NSSplitViewDelegate { 13 | 14 | @IBOutlet weak var mainWindow: NSWindow! 15 | @IBOutlet weak var titleBlurView: NSView! 16 | @IBOutlet weak var sourceListSuperView: NSView! 17 | @IBOutlet weak var fakeRightSplitter: NSView! 18 | @IBOutlet weak var rightColumnView: NSView! 19 | 20 | @IBOutlet weak var itemTitle: NSTextField! 21 | @IBOutlet weak var sectionTitle: NSTextField! 22 | 23 | @IBOutlet weak var gridAndDetailSplitter: NSSplitView! 24 | 25 | @IBOutlet weak var giphyLogo: NSImageView! 26 | 27 | @objc public func showGiphyLogo(show:ObjCBool) { 28 | giphyLogo.hidden = !show.boolValue 29 | } 30 | 31 | override public func awakeFromNib() { 32 | guard let content = mainWindow.contentView else { return } 33 | content.addSubview(self.titleBlurView); 34 | 35 | NSNotificationCenter.defaultCenter().addObserver(self, selector: "updateToolbarBlur", name: NSWindowDidResizeNotification, object: self.mainWindow) 36 | updateToolbarBlur() 37 | 38 | giphyLogo.animates = true 39 | 40 | var path = NSBundle.mainBundle().pathForResource("Giphy_API_Logo_ForWhite_Trans", ofType: "gif")! 41 | giphyLogo.image = NSImage(contentsOfFile: path) 42 | 43 | } 44 | 45 | public func splitView(splitView: NSSplitView, constrainMinCoordinate proposedMinimumPosition: CGFloat, ofSubviewAt dividerIndex: Int) -> CGFloat { 46 | return (splitView == self.fakeRightSplitter && dividerIndex == 0) ? 180 : proposedMinimumPosition; 47 | } 48 | 49 | public func splitView(splitView: NSSplitView, constrainMaxCoordinate proposedMaximumPosition: CGFloat, ofSubviewAt dividerIndex: Int) -> CGFloat { 50 | return (splitView == self.fakeRightSplitter && dividerIndex == 0) ? 240 : proposedMaximumPosition; 51 | } 52 | 53 | public func splitView(splitView: NSSplitView, constrainSplitPosition proposedPosition: CGFloat, ofSubviewAt dividerIndex: Int) -> CGFloat { 54 | self.updateToolbarBlur(); 55 | return proposedPosition; 56 | } 57 | 58 | func updateToolbarBlur(){ 59 | let windowFrame = self.titleBlurView.window?.frame 60 | let leftColumnWidth = CGRectGetWidth(self.sourceListSuperView.frame) 61 | let titleWidth = CGRectGetWidth(windowFrame!) - leftColumnWidth 62 | let titleHeight = CGRectGetHeight(self.titleBlurView.bounds) 63 | let rightColumnWidth = CGRectGetWidth(self.rightColumnView.frame) 64 | 65 | self.titleBlurView.frame = CGRectMake(leftColumnWidth, CGRectGetHeight(windowFrame!) - titleHeight + 1, titleWidth , titleHeight); 66 | 67 | self.fakeRightSplitter.frame = CGRectMake(titleWidth - rightColumnWidth, 0, 1, titleHeight) 68 | 69 | self.itemTitle.frame = CGRectMake(titleWidth - rightColumnWidth + 2, 18, rightColumnWidth , 18); 70 | 71 | self.sectionTitle.frame = CGRectMake(0, 18, titleWidth - rightColumnWidth, 18); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /objc/GIFs/Reddit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orta/GIFs/de70a076ca6c695319b047f4cd002b6e162b61f9/objc/GIFs/Reddit.png -------------------------------------------------------------------------------- /objc/GIFs/Reddit@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orta/GIFs/de70a076ca6c695319b047f4cd002b6e162b61f9/objc/GIFs/Reddit@2x.png -------------------------------------------------------------------------------- /objc/GIFs/RedditWhite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orta/GIFs/de70a076ca6c695319b047f4cd002b6e162b61f9/objc/GIFs/RedditWhite.png -------------------------------------------------------------------------------- /objc/GIFs/RedditWhite@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orta/GIFs/de70a076ca6c695319b047f4cd002b6e162b61f9/objc/GIFs/RedditWhite@2x.png -------------------------------------------------------------------------------- /objc/GIFs/SBSystemPreferences.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SBSystemPreferences.h 3 | */ 4 | 5 | #import 6 | #import 7 | 8 | 9 | @class SBSystemPreferencesItem, SBSystemPreferencesApplication, SBSystemPreferencesColor, SBSystemPreferencesDocument, SBSystemPreferencesWindow, SBSystemPreferencesAttributeRun, SBSystemPreferencesCharacter, SBSystemPreferencesParagraph, SBSystemPreferencesText, SBSystemPreferencesAttachment, SBSystemPreferencesWord, SBSystemPreferencesAnchor, SBSystemPreferencesPane, SBSystemPreferencesPrintSettings; 10 | 11 | enum SBSystemPreferencesSavo { 12 | SBSystemPreferencesSavoAsk = 'ask ' /* Ask the user whether or not to save the file. */, 13 | SBSystemPreferencesSavoNo = 'no ' /* Do not save the file. */, 14 | SBSystemPreferencesSavoYes = 'yes ' /* Save the file. */ 15 | }; 16 | typedef enum SBSystemPreferencesSavo SBSystemPreferencesSavo; 17 | 18 | enum SBSystemPreferencesEnum { 19 | SBSystemPreferencesEnumStandard = 'lwst' /* Standard PostScript error handling */, 20 | SBSystemPreferencesEnumDetailed = 'lwdt' /* print a detailed report of PostScript errors */ 21 | }; 22 | typedef enum SBSystemPreferencesEnum SBSystemPreferencesEnum; 23 | 24 | 25 | 26 | /* 27 | * Standard Suite 28 | */ 29 | 30 | // A scriptable object. 31 | @interface SBSystemPreferencesItem : SBObject 32 | 33 | @property (copy) NSDictionary *properties; // All of the object's properties. 34 | 35 | - (void) closeSaving:(SBSystemPreferencesSavo)saving savingIn:(NSURL *)savingIn; // Close an object. 36 | - (void) delete; // Delete an object. 37 | - (void) duplicateTo:(SBObject *)to withProperties:(NSDictionary *)withProperties; // Copy object(s) and put the copies at a new location. 38 | - (BOOL) exists; // Verify if an object exists. 39 | - (void) moveTo:(SBObject *)to; // Move object(s) to a new location. 40 | - (void) saveAs:(NSString *)as in:(NSURL *)in_; // Save an object. 41 | 42 | @end 43 | 44 | // An application's top level scripting object. 45 | @interface SBSystemPreferencesApplication : SBApplication 46 | 47 | - (SBElementArray *) documents; 48 | - (SBElementArray *) windows; 49 | 50 | @property (readonly) BOOL frontmost; // Is this the frontmost (active) application? 51 | @property (copy, readonly) NSString *name; // The name of the application. 52 | @property (copy, readonly) NSString *version; // The version of the application. 53 | 54 | - (SBSystemPreferencesDocument *) open:(NSURL *)x; // Open an object. 55 | - (void) print:(NSURL *)x printDialog:(BOOL)printDialog withProperties:(SBSystemPreferencesPrintSettings *)withProperties; // Print an object. 56 | - (void) quitSaving:(SBSystemPreferencesSavo)saving; // Quit an application. 57 | 58 | @end 59 | 60 | // A color. 61 | @interface SBSystemPreferencesColor : SBSystemPreferencesItem 62 | 63 | 64 | @end 65 | 66 | // A document. 67 | @interface SBSystemPreferencesDocument : SBSystemPreferencesItem 68 | 69 | @property (readonly) BOOL modified; // Has the document been modified since the last save? 70 | @property (copy) NSString *name; // The document's name. 71 | @property (copy) NSString *path; // The document's path. 72 | 73 | 74 | @end 75 | 76 | // A window. 77 | @interface SBSystemPreferencesWindow : SBSystemPreferencesItem 78 | 79 | @property NSRect bounds; // The bounding rectangle of the window. 80 | @property (readonly) BOOL closeable; // Whether the window has a close box. 81 | @property (copy, readonly) SBSystemPreferencesDocument *document; // The document whose contents are being displayed in the window. 82 | @property (readonly) BOOL floating; // Whether the window floats. 83 | - (NSInteger) id; // The unique identifier of the window. 84 | @property NSInteger index; // The index of the window, ordered front to back. 85 | @property (readonly) BOOL miniaturizable; // Whether the window can be miniaturized. 86 | @property BOOL miniaturized; // Whether the window is currently miniaturized. 87 | @property (readonly) BOOL modal; // Whether the window is the application's current modal window. 88 | @property (copy) NSString *name; // The full title of the window. 89 | @property (readonly) BOOL resizable; // Whether the window can be resized. 90 | @property (readonly) BOOL titled; // Whether the window has a title bar. 91 | @property BOOL visible; // Whether the window is currently visible. 92 | @property (readonly) BOOL zoomable; // Whether the window can be zoomed. 93 | @property BOOL zoomed; // Whether the window is currently zoomed. 94 | 95 | 96 | @end 97 | 98 | 99 | 100 | /* 101 | * Text Suite 102 | */ 103 | 104 | // This subdivides the text into chunks that all have the same attributes. 105 | @interface SBSystemPreferencesAttributeRun : SBSystemPreferencesItem 106 | 107 | - (SBElementArray *) attachments; 108 | - (SBElementArray *) attributeRuns; 109 | - (SBElementArray *) characters; 110 | - (SBElementArray *) paragraphs; 111 | - (SBElementArray *) words; 112 | 113 | @property (copy) NSColor *color; // The color of the first character. 114 | @property (copy) NSString *font; // The name of the font of the first character. 115 | @property NSInteger size; // The size in points of the first character. 116 | 117 | 118 | @end 119 | 120 | // This subdivides the text into characters. 121 | @interface SBSystemPreferencesCharacter : SBSystemPreferencesItem 122 | 123 | - (SBElementArray *) attachments; 124 | - (SBElementArray *) attributeRuns; 125 | - (SBElementArray *) characters; 126 | - (SBElementArray *) paragraphs; 127 | - (SBElementArray *) words; 128 | 129 | @property (copy) NSColor *color; // The color of the first character. 130 | @property (copy) NSString *font; // The name of the font of the first character. 131 | @property NSInteger size; // The size in points of the first character. 132 | 133 | 134 | @end 135 | 136 | // This subdivides the text into paragraphs. 137 | @interface SBSystemPreferencesParagraph : SBSystemPreferencesItem 138 | 139 | - (SBElementArray *) attachments; 140 | - (SBElementArray *) attributeRuns; 141 | - (SBElementArray *) characters; 142 | - (SBElementArray *) paragraphs; 143 | - (SBElementArray *) words; 144 | 145 | @property (copy) NSColor *color; // The color of the first character. 146 | @property (copy) NSString *font; // The name of the font of the first character. 147 | @property NSInteger size; // The size in points of the first character. 148 | 149 | 150 | @end 151 | 152 | // Rich (styled) text 153 | @interface SBSystemPreferencesText : SBSystemPreferencesItem 154 | 155 | - (SBElementArray *) attachments; 156 | - (SBElementArray *) attributeRuns; 157 | - (SBElementArray *) characters; 158 | - (SBElementArray *) paragraphs; 159 | - (SBElementArray *) words; 160 | 161 | @property (copy) NSColor *color; // The color of the first character. 162 | @property (copy) NSString *font; // The name of the font of the first character. 163 | @property NSInteger size; // The size in points of the first character. 164 | 165 | 166 | @end 167 | 168 | // Represents an inline text attachment. This class is used mainly for make commands. 169 | @interface SBSystemPreferencesAttachment : SBSystemPreferencesText 170 | 171 | @property (copy) NSString *fileName; // The path to the file for the attachment 172 | 173 | 174 | @end 175 | 176 | // This subdivides the text into words. 177 | @interface SBSystemPreferencesWord : SBSystemPreferencesItem 178 | 179 | - (SBElementArray *) attachments; 180 | - (SBElementArray *) attributeRuns; 181 | - (SBElementArray *) characters; 182 | - (SBElementArray *) paragraphs; 183 | - (SBElementArray *) words; 184 | 185 | @property (copy) NSColor *color; // The color of the first character. 186 | @property (copy) NSString *font; // The name of the font of the first character. 187 | @property NSInteger size; // The size in points of the first character. 188 | 189 | 190 | @end 191 | 192 | 193 | 194 | /* 195 | * System Preferences 196 | */ 197 | 198 | // an anchor within a preference pane 199 | @interface SBSystemPreferencesAnchor : SBSystemPreferencesItem 200 | 201 | @property (copy, readonly) NSString *name; // name of the anchor within a preference pane 202 | 203 | - (SBSystemPreferencesAnchor *) reveal; // Reveals an anchor within a preference pane or preference pane itself 204 | 205 | @end 206 | 207 | // System Preferences top level scripting object 208 | @interface SBSystemPreferencesApplication (SystemPreferences) 209 | 210 | - (SBElementArray *) panes; 211 | 212 | @property (copy) SBSystemPreferencesPane *currentPane; // the currently selected pane 213 | @property (copy, readonly) SBSystemPreferencesWindow *preferencesWindow; // the main preferences window 214 | @property BOOL showAll; // Is SystemPrefs in show all view. (Setting to false will do nothing) 215 | 216 | @end 217 | 218 | // a preference pane 219 | @interface SBSystemPreferencesPane : SBSystemPreferencesItem 220 | 221 | - (SBElementArray *) anchors; 222 | 223 | - (NSString *) id; // locale independent name of the preference pane; can refer to a pane using the expression: pane id "" 224 | @property (copy, readonly) NSString *localizedName; // localized name of the preference pane 225 | @property (copy, readonly) NSString *name; // name of the preference pane as it appears in the title bar; can refer to a pane using the expression: pane "" 226 | 227 | - (double) timedLoad; // Times and loads given preference pane and returns load time. 228 | 229 | @end 230 | 231 | 232 | 233 | /* 234 | * Type Definitions 235 | */ 236 | 237 | @interface SBSystemPreferencesPrintSettings : SBObject 238 | 239 | @property NSInteger copies; // the number of copies of a document to be printed 240 | @property BOOL collating; // Should printed copies be collated? 241 | @property NSInteger startingPage; // the first page of the document to be printed 242 | @property NSInteger endingPage; // the last page of the document to be printed 243 | @property NSInteger pagesAcross; // number of logical pages laid across a physical page 244 | @property NSInteger pagesDown; // number of logical pages laid out down a physical page 245 | @property (copy) NSDate *requestedPrintTime; // the time at which the desktop printer should print the document 246 | @property SBSystemPreferencesEnum errorHandling; // how errors are handled 247 | @property (copy) NSString *faxNumber; // for fax number 248 | @property (copy) NSString *targetPrinter; // for target printer 249 | 250 | - (void) closeSaving:(SBSystemPreferencesSavo)saving savingIn:(NSURL *)savingIn; // Close an object. 251 | - (void) delete; // Delete an object. 252 | - (void) duplicateTo:(SBObject *)to withProperties:(NSDictionary *)withProperties; // Copy object(s) and put the copies at a new location. 253 | - (BOOL) exists; // Verify if an object exists. 254 | - (void) moveTo:(SBObject *)to; // Move object(s) to a new location. 255 | - (void) saveAs:(NSString *)as in:(NSURL *)in_; // Save an object. 256 | 257 | @end 258 | 259 | -------------------------------------------------------------------------------- /objc/GIFs/Search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orta/GIFs/de70a076ca6c695319b047f4cd002b6e162b61f9/objc/GIFs/Search.png -------------------------------------------------------------------------------- /objc/GIFs/Search@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orta/GIFs/de70a076ca6c695319b047f4cd002b6e162b61f9/objc/GIFs/Search@2x.png -------------------------------------------------------------------------------- /objc/GIFs/SearchWhite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orta/GIFs/de70a076ca6c695319b047f4cd002b6e162b61f9/objc/GIFs/SearchWhite.png -------------------------------------------------------------------------------- /objc/GIFs/SearchWhite@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orta/GIFs/de70a076ca6c695319b047f4cd002b6e162b61f9/objc/GIFs/SearchWhite@2x.png -------------------------------------------------------------------------------- /objc/GIFs/ShadowView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ShadowView.swift 3 | // GIFs 4 | // 5 | // Created by Orta on 8/7/14. 6 | // Copyright (c) 2014 Orta Therox. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class ShadowView: NSView { 12 | override func drawRect(dirtyRect: NSRect) { 13 | super.drawRect(dirtyRect) 14 | NSColor(patternImage: NSImage(named: "shadow")!).set(); 15 | NSRectFill(dirtyRect); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /objc/GIFs/TwitterBird.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orta/GIFs/de70a076ca6c695319b047f4cd002b6e162b61f9/objc/GIFs/TwitterBird.png -------------------------------------------------------------------------------- /objc/GIFs/about0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orta/GIFs/de70a076ca6c695319b047f4cd002b6e162b61f9/objc/GIFs/about0.jpg -------------------------------------------------------------------------------- /objc/GIFs/about1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orta/GIFs/de70a076ca6c695319b047f4cd002b6e162b61f9/objc/GIFs/about1.jpg -------------------------------------------------------------------------------- /objc/GIFs/about10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orta/GIFs/de70a076ca6c695319b047f4cd002b6e162b61f9/objc/GIFs/about10.jpg -------------------------------------------------------------------------------- /objc/GIFs/about11.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orta/GIFs/de70a076ca6c695319b047f4cd002b6e162b61f9/objc/GIFs/about11.jpg -------------------------------------------------------------------------------- /objc/GIFs/about12.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orta/GIFs/de70a076ca6c695319b047f4cd002b6e162b61f9/objc/GIFs/about12.jpg -------------------------------------------------------------------------------- /objc/GIFs/about13.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orta/GIFs/de70a076ca6c695319b047f4cd002b6e162b61f9/objc/GIFs/about13.jpg -------------------------------------------------------------------------------- /objc/GIFs/about14.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orta/GIFs/de70a076ca6c695319b047f4cd002b6e162b61f9/objc/GIFs/about14.jpg -------------------------------------------------------------------------------- /objc/GIFs/about2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orta/GIFs/de70a076ca6c695319b047f4cd002b6e162b61f9/objc/GIFs/about2.jpg -------------------------------------------------------------------------------- /objc/GIFs/about3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orta/GIFs/de70a076ca6c695319b047f4cd002b6e162b61f9/objc/GIFs/about3.jpg -------------------------------------------------------------------------------- /objc/GIFs/about4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orta/GIFs/de70a076ca6c695319b047f4cd002b6e162b61f9/objc/GIFs/about4.jpg -------------------------------------------------------------------------------- /objc/GIFs/about5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orta/GIFs/de70a076ca6c695319b047f4cd002b6e162b61f9/objc/GIFs/about5.jpg -------------------------------------------------------------------------------- /objc/GIFs/about6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orta/GIFs/de70a076ca6c695319b047f4cd002b6e162b61f9/objc/GIFs/about6.jpg -------------------------------------------------------------------------------- /objc/GIFs/about7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orta/GIFs/de70a076ca6c695319b047f4cd002b6e162b61f9/objc/GIFs/about7.jpg -------------------------------------------------------------------------------- /objc/GIFs/about8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orta/GIFs/de70a076ca6c695319b047f4cd002b6e162b61f9/objc/GIFs/about8.jpg -------------------------------------------------------------------------------- /objc/GIFs/about9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orta/GIFs/de70a076ca6c695319b047f4cd002b6e162b61f9/objc/GIFs/about9.jpg -------------------------------------------------------------------------------- /objc/GIFs/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orta/GIFs/de70a076ca6c695319b047f4cd002b6e162b61f9/objc/GIFs/close.png -------------------------------------------------------------------------------- /objc/GIFs/close@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orta/GIFs/de70a076ca6c695319b047f4cd002b6e162b61f9/objc/GIFs/close@2x.png -------------------------------------------------------------------------------- /objc/GIFs/close_active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orta/GIFs/de70a076ca6c695319b047f4cd002b6e162b61f9/objc/GIFs/close_active.png -------------------------------------------------------------------------------- /objc/GIFs/close_active@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orta/GIFs/de70a076ca6c695319b047f4cd002b6e162b61f9/objc/GIFs/close_active@2x.png -------------------------------------------------------------------------------- /objc/GIFs/en.lproj/Credits.rtf: -------------------------------------------------------------------------------- 1 | {\rtf0\ansi{\fonttbl\f0\fswiss Helvetica;} 2 | {\colortbl;\red255\green255\blue255;} 3 | \paperw9840\paperh8400 4 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural 5 | 6 | \f0\b\fs24 \cf0 Engineering: 7 | \b0 \ 8 | Some people\ 9 | \ 10 | 11 | \b Human Interface Design: 12 | \b0 \ 13 | Some other people\ 14 | \ 15 | 16 | \b Testing: 17 | \b0 \ 18 | Hopefully not nobody\ 19 | \ 20 | 21 | \b Documentation: 22 | \b0 \ 23 | Whoever\ 24 | \ 25 | 26 | \b With special thanks to: 27 | \b0 \ 28 | Mom\ 29 | } 30 | -------------------------------------------------------------------------------- /objc/GIFs/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /objc/GIFs/gif_template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 72 | 73 | 74 | 75 |
76 | 77 |
78 | 79 | 80 | -------------------------------------------------------------------------------- /objc/GIFs/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // GIFs 4 | // 5 | // Created by orta therox on 12/01/2013. 6 | // Copyright (c) 2013 Orta Therox. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | int main(int argc, char *argv[]) 12 | { 13 | return NSApplicationMain(argc, (const char **)argv); 14 | } 15 | -------------------------------------------------------------------------------- /objc/GIFs/more.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orta/GIFs/de70a076ca6c695319b047f4cd002b6e162b61f9/objc/GIFs/more.png -------------------------------------------------------------------------------- /objc/GIFs/plus_active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orta/GIFs/de70a076ca6c695319b047f4cd002b6e162b61f9/objc/GIFs/plus_active.png -------------------------------------------------------------------------------- /objc/GIFs/plus_active@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orta/GIFs/de70a076ca6c695319b047f4cd002b6e162b61f9/objc/GIFs/plus_active@2x.png -------------------------------------------------------------------------------- /objc/GIFs/plus_active@2x.pxm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orta/GIFs/de70a076ca6c695319b047f4cd002b6e162b61f9/objc/GIFs/plus_active@2x.pxm -------------------------------------------------------------------------------- /objc/GIFs/shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orta/GIFs/de70a076ca6c695319b047f4cd002b6e162b61f9/objc/GIFs/shadow.png -------------------------------------------------------------------------------- /objc/GIFs/tick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orta/GIFs/de70a076ca6c695319b047f4cd002b6e162b61f9/objc/GIFs/tick.png -------------------------------------------------------------------------------- /objc/GIFs/tick@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orta/GIFs/de70a076ca6c695319b047f4cd002b6e162b61f9/objc/GIFs/tick@2x.png -------------------------------------------------------------------------------- /objc/GIFs/tick_active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orta/GIFs/de70a076ca6c695319b047f4cd002b6e162b61f9/objc/GIFs/tick_active.png -------------------------------------------------------------------------------- /objc/GIFs/tick_active@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orta/GIFs/de70a076ca6c695319b047f4cd002b6e162b61f9/objc/GIFs/tick_active@2x.png -------------------------------------------------------------------------------- /objc/GIFs/tumblr_t.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orta/GIFs/de70a076ca6c695319b047f4cd002b6e162b61f9/objc/GIFs/tumblr_t.png -------------------------------------------------------------------------------- /objc/GIFs/tumblr_t@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orta/GIFs/de70a076ca6c695319b047f4cd002b6e162b61f9/objc/GIFs/tumblr_t@2x.png -------------------------------------------------------------------------------- /objc/GIFs/tumblr_t_active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orta/GIFs/de70a076ca6c695319b047f4cd002b6e162b61f9/objc/GIFs/tumblr_t_active.png -------------------------------------------------------------------------------- /objc/GIFs/tumblr_t_active@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orta/GIFs/de70a076ca6c695319b047f4cd002b6e162b61f9/objc/GIFs/tumblr_t_active@2x.png -------------------------------------------------------------------------------- /objc/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem "cocoapods", "1.1.0.rc.2" 4 | gem "cocoapods-keys" 5 | gem "cocoapods-deintegrate" 6 | -------------------------------------------------------------------------------- /objc/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | RubyInline (3.12.4) 5 | ZenTest (~> 4.3) 6 | ZenTest (4.11.1) 7 | activesupport (4.2.7.1) 8 | i18n (~> 0.7) 9 | json (~> 1.7, >= 1.7.7) 10 | minitest (~> 5.1) 11 | thread_safe (~> 0.3, >= 0.3.4) 12 | tzinfo (~> 1.1) 13 | claide (1.0.0) 14 | cocoapods (1.1.0.rc.2) 15 | activesupport (>= 4.0.2, < 5) 16 | claide (>= 1.0.0, < 2.0) 17 | cocoapods-core (= 1.1.0.rc.2) 18 | cocoapods-deintegrate (>= 1.0.1, < 2.0) 19 | cocoapods-downloader (>= 1.1.1, < 2.0) 20 | cocoapods-plugins (>= 1.0.0, < 2.0) 21 | cocoapods-search (>= 1.0.0, < 2.0) 22 | cocoapods-stats (>= 1.0.0, < 2.0) 23 | cocoapods-trunk (>= 1.0.0, < 2.0) 24 | cocoapods-try (>= 1.1.0, < 2.0) 25 | colored (~> 1.2) 26 | escape (~> 0.0.4) 27 | fourflusher (~> 1.0.1) 28 | gh_inspector (~> 1.0) 29 | molinillo (~> 0.5.1) 30 | nap (~> 1.0) 31 | xcodeproj (>= 1.3.1, < 2.0) 32 | cocoapods-core (1.1.0.rc.2) 33 | activesupport (>= 4.0.2, < 5) 34 | fuzzy_match (~> 2.0.4) 35 | nap (~> 1.0) 36 | cocoapods-deintegrate (1.0.1) 37 | cocoapods-downloader (1.1.1) 38 | cocoapods-keys (1.7.0) 39 | dotenv 40 | osx_keychain 41 | cocoapods-plugins (1.0.0) 42 | nap 43 | cocoapods-search (1.0.0) 44 | cocoapods-stats (1.0.0) 45 | cocoapods-trunk (1.0.0) 46 | nap (>= 0.8, < 2.0) 47 | netrc (= 0.7.8) 48 | cocoapods-try (1.1.0) 49 | colored (1.2) 50 | dotenv (2.1.1) 51 | escape (0.0.4) 52 | fourflusher (1.0.1) 53 | fuzzy_match (2.0.4) 54 | gh_inspector (1.0.2) 55 | i18n (0.7.0) 56 | json (1.8.3) 57 | minitest (5.9.0) 58 | molinillo (0.5.1) 59 | nap (1.1.0) 60 | netrc (0.7.8) 61 | osx_keychain (1.0.1) 62 | RubyInline (~> 3) 63 | thread_safe (0.3.5) 64 | tzinfo (1.2.2) 65 | thread_safe (~> 0.1) 66 | xcodeproj (1.3.1) 67 | activesupport (>= 3) 68 | claide (>= 1.0.0, < 2.0) 69 | colored (~> 1.2) 70 | 71 | PLATFORMS 72 | ruby 73 | 74 | DEPENDENCIES 75 | cocoapods (= 1.1.0.rc.2) 76 | cocoapods-deintegrate 77 | cocoapods-keys 78 | 79 | BUNDLED WITH 80 | 1.12.5 81 | -------------------------------------------------------------------------------- /objc/Makefile: -------------------------------------------------------------------------------- 1 | bootstrap: 2 | bundle install 3 | @echo "\nSetting up API Keys, leave blank if you don't know." 4 | @printf '\nWhat is your Giphy API Key ( use the one on https://github.com/Giphy/GiphyAPI if you do not have one )? '; \ 5 | read GIPHY_API_KEY; \ 6 | 7 | bundle exec pod keys set GiphyAPIKey "$$GIPHY_API_KEY" GIFs 8 | bundle exec pod keys set GiphyAPIKey "$$GIPHY_API_KEY" GIFs 9 | bundle exec pod keys set RandoTwitterBotConsumerKey "" GIFs 10 | bundle exec pod keys set RandoTwitterBotConsumerSecret "" GIFs 11 | bundle exec pod keys set RandoTwitterBotOAuthToken "" GIFs 12 | bundle exec pod keys set RandoTwitterBotOAuthTokenSecret "" GIFs 13 | 14 | bundle exec pod install 15 | -------------------------------------------------------------------------------- /objc/Podfile: -------------------------------------------------------------------------------- 1 | platform :osx, "10.9" 2 | 3 | plugin 'cocoapods-keys', { 4 | :project => "GIFs", 5 | :keys => [ 6 | "GiphyAPIKey", 7 | "RandoTwitterBotConsumerKey", 8 | "RandoTwitterBotConsumerSecret", 9 | "RandoTwitterBotOAuthToken", 10 | "RandoTwitterBotOAuthTokenSecret" 11 | ] 12 | } 13 | 14 | target "GIFs" do 15 | pod 'StandardPaths', '~> 1.5.4' 16 | pod "GIFKit", :git => "https://github.com/orta/GIFKit.git" 17 | pod 'STTwitter', '~> 0.2' 18 | end -------------------------------------------------------------------------------- /objc/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - AFNetworking (2.6.3): 3 | - AFNetworking/NSURLConnection (= 2.6.3) 4 | - AFNetworking/NSURLSession (= 2.6.3) 5 | - AFNetworking/Reachability (= 2.6.3) 6 | - AFNetworking/Security (= 2.6.3) 7 | - AFNetworking/Serialization (= 2.6.3) 8 | - AFNetworking/UIKit (= 2.6.3) 9 | - AFNetworking/NSURLConnection (2.6.3): 10 | - AFNetworking/Reachability 11 | - AFNetworking/Security 12 | - AFNetworking/Serialization 13 | - AFNetworking/NSURLSession (2.6.3): 14 | - AFNetworking/Reachability 15 | - AFNetworking/Security 16 | - AFNetworking/Serialization 17 | - AFNetworking/Reachability (2.6.3) 18 | - AFNetworking/Security (2.6.3) 19 | - AFNetworking/Serialization (2.6.3) 20 | - GIFKit (0.1.0): 21 | - AFNetworking (~> 2.3) 22 | - Keys (1.0.0) 23 | - StandardPaths (1.5.6) 24 | - STTwitter (0.2.5) 25 | 26 | DEPENDENCIES: 27 | - GIFKit (from `https://github.com/orta/GIFKit.git`) 28 | - Keys (from `Pods/CocoaPodsKeys`) 29 | - StandardPaths (~> 1.5.4) 30 | - STTwitter (~> 0.2) 31 | 32 | EXTERNAL SOURCES: 33 | GIFKit: 34 | :git: https://github.com/orta/GIFKit.git 35 | Keys: 36 | :path: Pods/CocoaPodsKeys 37 | 38 | CHECKOUT OPTIONS: 39 | GIFKit: 40 | :commit: f9b71bfce26cb61bbad3aa890c539699e924e9af 41 | :git: https://github.com/orta/GIFKit.git 42 | 43 | SPEC CHECKSUMS: 44 | AFNetworking: cb8d14a848e831097108418f5d49217339d4eb60 45 | GIFKit: 2588e9225ea99351a3525b2205605ab1b9307866 46 | Keys: 9c35bf00f612ee1d48556f4a4b9b4551e224e90f 47 | StandardPaths: 0c11c9256a68dd2339d4d2de647a5bd819648c46 48 | STTwitter: 5c157e8dcb4f0e4cf5b4374393021b7d7b7fd497 49 | 50 | PODFILE CHECKSUM: ef1686754bd76bb3917f80e90a403ab3227659e0 51 | 52 | COCOAPODS: 1.1.0.rc.2 53 | -------------------------------------------------------------------------------- /web/GIFs.app.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orta/GIFs/de70a076ca6c695319b047f4cd002b6e162b61f9/web/GIFs.app.zip -------------------------------------------------------------------------------- /web/yosemite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orta/GIFs/de70a076ca6c695319b047f4cd002b6e162b61f9/web/yosemite.png --------------------------------------------------------------------------------