├── .gitignore
├── .travis.yml
├── LICENSE.txt
├── README.md
├── SPECIFICATION.md
├── browser
├── SwipeBrowser.swift
├── SwipeExporter.swift
├── SwipeTableViewController.swift
├── ios
│ ├── SwipeBrowser.xib
│ └── SwipeTableViewController.xib
└── tvos
│ ├── SwipeBrowser.xib
│ └── SwipeTableViewController.xib
├── core
├── SwipeAction.swift
├── SwipeBook.swift
├── SwipeDocumentViewer.swift
├── SwipeElement.swift
├── SwipeEvent.swift
├── SwipeEventHandler.swift
├── SwipeExtensions.swift
├── SwipeHttpGet.swift
├── SwipeHttpPost.swift
├── SwipeList.swift
├── SwipeMarkdown.swift
├── SwipeNode.swift
├── SwipePage.swift
├── SwipePageTemplate.swift
├── SwipeParser.swift
├── SwipePath.swift
├── SwipeSynthesizer.swift
├── SwipeTextArea.swift
├── SwipeTextField.swift
├── SwipeTimer.swift
├── SwipeView.swift
└── SwipeViewController.swift
├── network
├── SNNotificationManager.swift
├── SwipeAssetManager.swift
├── SwipeConnection.swift
├── SwipePrefetcher.swift
└── asset.xcdatamodeld
│ └── asset.xcdatamodel
│ └── contents
└── sample
├── bug_repro
├── AppDelegate.swift
├── Assets.xcassets
│ └── AppIcon.appiconset
│ │ └── Contents.json
├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
├── IMG_9401.mov
├── Info.plist
└── ViewController.swift
├── sample.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcuserdata
│ │ └── satoshi.xcuserdatad
│ │ └── UserInterfaceState.xcuserstate
├── xcshareddata
│ └── xcschemes
│ │ ├── sample.xcscheme
│ │ └── sampletv.xcscheme
└── xcuserdata
│ └── satoshi.xcuserdatad
│ └── xcschemes
│ ├── sample.xcscheme
│ └── xcschememanagement.plist
├── sample
├── AppDelegate.swift
├── Assets.xcassets
│ └── AppIcon.appiconset
│ │ └── Contents.json
├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
├── Info.plist
├── SwipeBrowser+ex.swift
├── SwipeBrowser.xib
├── SwipeVideoController.swift
├── SwipeVideoController.xib
├── iTunesArtwork@2x.png
├── more.png
├── movie.png
├── nasa.png
├── radio.png
├── speech.png
└── text.png
├── sampletv
├── AppDelegate.swift
├── Assets.xcassets
│ ├── App Icon & Top Shelf Image.brandassets
│ │ ├── App Icon - Large.imagestack
│ │ │ ├── Back.imagestacklayer
│ │ │ │ ├── Content.imageset
│ │ │ │ │ └── Contents.json
│ │ │ │ └── Contents.json
│ │ │ ├── Contents.json
│ │ │ ├── Front.imagestacklayer
│ │ │ │ ├── Content.imageset
│ │ │ │ │ └── Contents.json
│ │ │ │ └── Contents.json
│ │ │ └── Middle.imagestacklayer
│ │ │ │ ├── Content.imageset
│ │ │ │ └── Contents.json
│ │ │ │ └── Contents.json
│ │ ├── App Icon - Small.imagestack
│ │ │ ├── Back.imagestacklayer
│ │ │ │ ├── Content.imageset
│ │ │ │ │ └── Contents.json
│ │ │ │ └── Contents.json
│ │ │ ├── Contents.json
│ │ │ ├── Front.imagestacklayer
│ │ │ │ ├── Content.imageset
│ │ │ │ │ └── Contents.json
│ │ │ │ └── Contents.json
│ │ │ └── Middle.imagestacklayer
│ │ │ │ ├── Content.imageset
│ │ │ │ └── Contents.json
│ │ │ │ └── Contents.json
│ │ ├── Contents.json
│ │ └── Top Shelf Image.imageset
│ │ │ └── Contents.json
│ ├── Contents.json
│ └── LaunchImage.launchimage
│ │ └── Contents.json
├── Base.lproj
│ └── Main.storyboard
└── Info.plist
└── script
├── actions.swipe
├── bgm
├── HiddenAgenda.mp3
├── MonkeysSpinningMonkeys.mp3
├── TheDescent.mp3
└── music.swipe
├── details
├── details_index.swipe
├── loop_animation.swipe
├── multilingual_strings.swipe
└── transition_animation.swipe
├── espresso.png
├── espresso
├── IMG_8152.JPG
├── IMG_8153.m4v
├── IMG_8154.m4v
├── IMG_8155.JPG
├── IMG_8156.JPG
├── IMG_8159.m4v
├── IMG_8160.m4v
├── IMG_8161.JPG
├── IMG_8162.JPG
├── IMG_8163.m4v
├── IMG_8166.m4v
├── IMG_8167.m4v
├── IMG_8168.JPG
├── IMG_8169.m4v
├── IMG_8171.m4v
└── espresso_machine_l.swipe
├── index.swipe
├── list.swipe
├── markdown.swipe
├── nasa
├── nasa.swipe
├── nasa000.jpg
├── nasa001.jpg
├── nasa002.jpg
├── nasa003.jpg
├── nasa004.jpg
├── nasa005.jpg
├── nasa006.jpg
├── nasa007.jpg
├── nasa008.jpg
├── nasa009.jpg
├── nasa010.jpg
├── nasa011.jpg
├── nasa012.jpg
└── nasa013.jpg
├── network.swipe
├── radiostream.swipe
├── speech.swipe
├── tutorial
├── Icon-180.png
├── chara_walk.png
├── dice.swipe
├── dice2.swipe
├── empty.swipe
├── epsilon.gif
├── izumi_06.mov
├── shuttle.png
├── sound01.wav
├── swipe.swipe
└── tutorial.swipe
├── tv
├── focus.swipe
└── tv_index.swipe
├── update.swipe
├── vectors.swipe
└── videostream.swipe
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | *.pbxuser
3 | *.mode1v3
4 | *.mode2v3
5 | *.perspectivev3
6 | *.xcuserstate
7 | project.xcworkspace/
8 | xcuserdata/
9 |
10 | .DS_Store
11 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: objective-c
2 | osx_image: xcode8.1
3 | env:
4 | global:
5 | - LC_CTYPE=en_US.UTF-8
6 | - PROJECT=sample/sample.xcodeproj
7 | matrix:
8 | - SCHEME=sample SDK=iphonesimulator
9 | - SCHEME=sampletv SDK=appletvsimulator
10 | script:
11 | - set -o pipefail
12 | - xcodebuild build -project $PROJECT -scheme $SCHEME -sdk $SDK -configuration Release | xcpretty -c
13 | notifications:
14 | email: false
15 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Satoshi Nakajima (https://github.com/snakajima)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Swipe & Swipe Engine
2 |
3 | [](https://travis-ci.org/swipe-org/swipe)
4 |
5 | ## What is Swipe?
6 |
7 | Swipe is a **domain-specific, declarative language** for non-developers (such as designers, animators, illustrators, musicians, videographers and comic writers) to create **media-rich/animated documents** that contain photos, videos, images, vector graphics, animations, voices, musics and sound effects, which will be consumed on touch-enabled devices such as smartphones, tablets and touch-enabled set-top-boxes (such as **iPhone and Apple TV**).
8 |
9 | ## What is Swipe Engine?
10 |
11 | Swipe Engine is the **viewer** of documents described in Swipe. Since it supports hyperlinking and allows you to browse from one Swipe document to another, you could even call it a **domain-specific browser**. At this moment (October 2015), Swipe Engine is available for **iOS and tvOS**, but will be ported to other platforms such as Android, Windows and even HTML5 (and we are looking for volunteers to do so).
12 |
13 | ## Why do we need Swipe?
14 |
15 | Because existing platforms (such as HTML5, ePub, Flash and iBooks) require some form of programming to create media-rich/animated documents, which is expensive and error-prone.
16 |
17 | * HTML used be a simple declarative language, but became a full-blown programming environment with DOM and JavaScript.
18 | * Flash used be a great animation tool for designers, but became a full-blown programming environment with the introduction of ActionScript.
19 | * ePub does not directly support animations, and the author needs to embed some JavaScript code to enable animations, which may or may not work depending on the eBook platform.
20 |
21 | That's why I came to the conclusion that it's time to design a new platform, which is powerful enough to describe media-rich documents that allow us to take full advantage of modern devices, but is also **strictly declarative** (no API, no script), easy to read, easy to write and easy to auto-generate.
22 |
23 | ## Why is it open?
24 |
25 | Because I want to make it an industry standard so that everybody can benefit from it. I was lucky enough to work on several successful products, such as Windows 95, which were used by hundreds of millions of people. I'd like to see such a success again.
26 |
27 | ## Is it really free?
28 |
29 | Yes, I still maintain the copyright, but it's **absolutely free**. You can use it for non-commercial and commercial applications, modify it as needed, port it to other platforms (I'd really appreciate if you open source it as well), re-distribute it with your applications, as long as you explicitly mention that your application uses Swipe and recognize me (Satoshi Nakajima) as the copyright holder.
30 |
31 | ## Target Applications
32 |
33 | * Interactive Comics
34 | * Sound Novels
35 | * Graphical Audio Books and Music Albums
36 | * Interactive Videos
37 | * Media-rich Tutorials and Presentations
38 | * Interactive Arts
39 |
40 | ## Primary Audiences
41 |
42 | * Designers/Illustrators
43 | * Animators
44 | * Comic Writers
45 | * Musicians/Artists
46 | * Videographers/Photographers
47 | * Teachers/Educators
48 | * Weekend Programmers
49 |
50 | ## Design Principles
51 |
52 | * Optimized for touch-enabled devices
53 | * 100% declarative (no programming)
54 | * Rich & interactive animations
55 | * Re-invent the video experience
56 | * Customizable page-transitions
57 | * Designer friendly
58 | * Lightweight & portable
59 |
60 | ## Is there an authoring tool?
61 |
62 | I'm working on it. Stay tuned.
63 |
64 | ## Specification
65 |
66 | The specification is available [here](SPECIFICATION.md).
67 |
68 |
--------------------------------------------------------------------------------
/browser/SwipeExporter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwipeExporter.swift
3 | // sample
4 | //
5 | // Created by satoshi on 8/19/16.
6 | // Copyright © 2016 Satoshi Nakajima. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import ImageIO
11 | import MobileCoreServices
12 | import AVFoundation
13 |
14 | class SwipeExporter: NSObject {
15 | enum Error: Swift.Error {
16 | case FailedToCreate
17 | case FailedToFinalize
18 | }
19 |
20 | let swipeViewController:SwipeViewController
21 | let fps:Int
22 | let resolution:CGFloat
23 | var progress = 0.0 as CGFloat // Output: Proress from 0.0 to 1.0
24 | var outputSize = CGSize.zero // Output: Size of generated GIF/video
25 | var pauseDuration = 0.0 as CGFloat
26 | var transitionDuration = 1.0 as CGFloat
27 |
28 | private var iFrame = 0
29 |
30 | init(swipeViewController:SwipeViewController, fps:Int, resolution:CGFloat = 720.0) {
31 | self.swipeViewController = swipeViewController
32 | self.fps = fps
33 | self.resolution = resolution
34 | }
35 |
36 | func exportAsGifAnimation(_ fileURL:URL, startPage:Int, pageCount:Int, progress:@escaping (_ complete:Bool, _ error:Swift.Error?)->Void) {
37 | guard let idst = CGImageDestinationCreateWithURL(fileURL as CFURL, kUTTypeGIF, pageCount * fps + 1, nil) else {
38 | return progress(false, Error.FailedToCreate)
39 | }
40 | CGImageDestinationSetProperties(idst, [String(kCGImagePropertyGIFDictionary):
41 | [String(kCGImagePropertyGIFLoopCount):0]] as CFDictionary)
42 | iFrame = 0
43 | outputSize = swipeViewController.view.frame.size
44 | self.processFrame(idst, startPage:startPage, pageCount: pageCount, progress:progress)
45 | }
46 |
47 | func processFrame(_ idst:CGImageDestination, startPage:Int, pageCount:Int, progress:@escaping (_ complete:Bool, _ error:Swift.Error?)->Void) {
48 | self.progress = CGFloat(iFrame) / CGFloat(fps) / CGFloat(pageCount)
49 | swipeViewController.scrollTo(CGFloat(startPage) + CGFloat(iFrame) / CGFloat(fps))
50 |
51 | // HACK: This delay is not 100% reliable, but is sufficient practically.
52 | DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(100)) {
53 | progress(false, nil)
54 | let presentationLayer = self.swipeViewController.view.layer.presentation()!
55 | UIGraphicsBeginImageContext(self.swipeViewController.view.frame.size); defer {
56 | UIGraphicsEndImageContext()
57 | }
58 | presentationLayer.render(in: UIGraphicsGetCurrentContext()!)
59 | let image = UIGraphicsGetImageFromCurrentImageContext()!
60 |
61 | CGImageDestinationAddImage(idst, image.cgImage!, [String(kCGImagePropertyGIFDictionary):
62 | [String(kCGImagePropertyGIFDelayTime):0.2]] as CFDictionary)
63 |
64 | self.iFrame += 1
65 | if self.iFrame < pageCount * self.fps + 1 {
66 | self.processFrame(idst, startPage:startPage, pageCount: pageCount, progress:progress)
67 | } else {
68 | if CGImageDestinationFinalize(idst) {
69 | progress(true, nil)
70 | } else {
71 | progress(false, Error.FailedToFinalize)
72 | }
73 | }
74 | }
75 | }
76 |
77 | func exportAsMovie(_ fileURL:URL, startPage:Int, pageCount:Int?, progress:@escaping (_ complete:Bool, _ error:Swift.Error?)->Void) {
78 | // AVAssetWrite will fail if the file already exists
79 | let manager = FileManager.default
80 | if manager.fileExists(atPath: fileURL.path) {
81 | try! manager.removeItem(at: fileURL)
82 | }
83 |
84 | let efps = Int(round(CGFloat(fps) * transitionDuration)) // Effective FPS
85 | let extra = Int(round(CGFloat(fps) * pauseDuration))
86 |
87 | let limit:Int
88 | if let pageCount = pageCount, startPage + pageCount < swipeViewController.book.pages.count {
89 | limit = pageCount * (efps + extra) + extra + 1
90 | } else {
91 | limit = (swipeViewController.book.pages.count - startPage - 1) * (efps + extra) + extra + 1
92 | }
93 | print("SwipeExporter:exportAsMovie", self.fps, efps, extra, limit)
94 |
95 | let viewSize = swipeViewController.view.frame.size
96 | let scale = min(resolution / min(viewSize.width, viewSize.height), swipeViewController.view.contentScaleFactor)
97 |
98 | outputSize = CGSize(width: viewSize.width * scale, height: viewSize.height * scale)
99 |
100 | self.swipeViewController.scrollTo(CGFloat(startPage))
101 | DispatchQueue.main.async { // HACK: work-around of empty first page bug
102 | do {
103 | let writer = try AVAssetWriter(url: fileURL, fileType: AVFileType.mov)
104 | let input = AVAssetWriterInput(mediaType: AVMediaType.video, outputSettings: [
105 | AVVideoCodecKey : AVVideoCodecType.h264,
106 | AVVideoWidthKey : self.outputSize.width,
107 | AVVideoHeightKey : self.outputSize.height
108 | ])
109 | let adaptor = AVAssetWriterInputPixelBufferAdaptor(assetWriterInput: input, sourcePixelBufferAttributes: [
110 | kCVPixelBufferPixelFormatTypeKey as String: NSNumber(value: kCVPixelFormatType_32ARGB),
111 | kCVPixelBufferWidthKey as String: self.outputSize.width,
112 | kCVPixelBufferHeightKey as String: self.outputSize.height,
113 | ])
114 | writer.add(input)
115 |
116 | self.iFrame = 0
117 |
118 | guard writer.startWriting() else {
119 | return progress(false, Error.FailedToFinalize)
120 | }
121 | writer.startSession(atSourceTime: CMTimeMake(value:0, timescale:Int32(self.fps)))
122 |
123 | //self.swipeViewController.scrollTo(CGFloat(startPage))
124 | input.requestMediaDataWhenReady(on: DispatchQueue.main) {
125 | guard input.isReadyForMoreMediaData else {
126 | print("SwipeExporter:not ready", self.iFrame)
127 | return // Not ready. Just wait.
128 | }
129 | self.progress = 0.5 * CGFloat(self.iFrame) / CGFloat(limit)
130 | progress(false, nil)
131 |
132 | var pixelBufferX: CVPixelBuffer? = nil
133 | let status: CVReturn = CVPixelBufferPoolCreatePixelBuffer(kCFAllocatorDefault, adaptor.pixelBufferPool!, &pixelBufferX)
134 | guard let managedPixelBuffer = pixelBufferX, status == 0 else {
135 | print("failed to allocate pixel buffer")
136 | writer.cancelWriting()
137 | return progress(false, Error.FailedToCreate)
138 | }
139 |
140 | CVPixelBufferLockBaseAddress(managedPixelBuffer, CVPixelBufferLockFlags(rawValue: CVOptionFlags(0)))
141 | let data = CVPixelBufferGetBaseAddress(managedPixelBuffer)
142 | let rgbColorSpace = CGColorSpaceCreateDeviceRGB()
143 | if let context = CGContext(data: data, width: Int(self.outputSize.width), height: Int(self.outputSize.height), bitsPerComponent: 8, bytesPerRow: CVPixelBufferGetBytesPerRow(managedPixelBuffer), space: rgbColorSpace, bitmapInfo: CGImageAlphaInfo.premultipliedFirst.rawValue) {
144 | let xf = CGAffineTransform(scaleX: scale, y: -scale)
145 | context.concatenate(xf.translatedBy(x: 0, y: -viewSize.height))
146 | let presentationLayer = self.swipeViewController.view.layer.presentation()!
147 | presentationLayer.render(in: context)
148 | //print("SwipeExporter:render", self.iFrame)
149 | } else {
150 | print("SwipeExporter:failed to get context", self.iFrame, self.fps)
151 | }
152 | CVPixelBufferUnlockBaseAddress(managedPixelBuffer, CVPixelBufferLockFlags(rawValue: CVOptionFlags(0)))
153 |
154 | let presentationTime = CMTimeMake(value:Int64(self.iFrame), timescale: Int32(self.fps))
155 | if !adaptor.append(managedPixelBuffer, withPresentationTime: presentationTime) {
156 | print("SwipeExporter:failed to append", self.iFrame)
157 | writer.cancelWriting()
158 | return progress(false, Error.FailedToCreate)
159 | }
160 |
161 | self.iFrame += 1
162 | if self.iFrame < limit {
163 | let curPage = self.iFrame / (extra + efps)
164 | let offset = self.iFrame % (extra + efps)
165 | if offset == 0 {
166 | self.swipeViewController.scrollTo(CGFloat(startPage + curPage))
167 | } else if offset > extra {
168 | self.swipeViewController.scrollTo(CGFloat(startPage + curPage) + CGFloat(offset - extra) / CGFloat(efps))
169 | }
170 | } else {
171 | input.markAsFinished()
172 | print("SwipeExporter: finishWritingWithCompletionHandler")
173 | writer.finishWriting(completionHandler: {
174 | DispatchQueue.main.async {
175 | progress(true, nil)
176 | }
177 | })
178 | }
179 | }
180 | } catch let error {
181 | progress(false, error)
182 | }
183 | }
184 | }
185 | }
186 |
--------------------------------------------------------------------------------
/browser/ios/SwipeTableViewController.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/browser/tvos/SwipeBrowser.xib:
--------------------------------------------------------------------------------
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 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/browser/tvos/SwipeTableViewController.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/core/SwipeAction.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwipeAction.swift
3 | //
4 | // Created by Pete Stoppani on 5/19/16.
5 | //
6 |
7 | import Foundation
8 |
9 | class SwipeAction: NSObject {
10 |
11 | let info:[String:Any]
12 |
13 | init(info:[String:Any]) {
14 | self.info = info
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/core/SwipeDocumentViewer.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwipeDocumentViewer.swift
3 | // sample
4 | //
5 | // Created by satoshi on 10/13/15.
6 | // Copyright © 2015 Satoshi Nakajima. All rights reserved.
7 | //
8 |
9 | #if os(OSX)
10 | import Cocoa
11 | #else
12 | import UIKit
13 | #endif
14 |
15 | protocol SwipeDocumentViewerDelegate: NSObjectProtocol {
16 | func browseTo(_ url:URL)
17 | func tapped()
18 | }
19 |
20 | protocol SwipeDocumentViewer {
21 | func documentTitle() -> String?
22 | func loadDocument(_ document:[String:Any], size:CGSize, url:URL?, state:[String:Any]?, callback:@escaping (Float, NSError?)->(Void)) throws
23 | func hideUI() -> Bool
24 | func landscape() -> Bool
25 | func setDelegate(_ delegate:SwipeDocumentViewerDelegate)
26 | func becomeZombie()
27 | func saveState() -> [String:Any]?
28 | func languages() -> [[String:Any]]?
29 | func reloadWithLanguageId(_ langId:String)
30 | func moveToPageAt(index:Int)
31 | func pageIndex() -> Int?
32 | func pageCount() -> Int?
33 | }
34 |
35 | enum SwipeError: Swift.Error {
36 | case invalidDocument
37 | }
38 |
--------------------------------------------------------------------------------
/core/SwipeEvent.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwipeEvent.swift
3 | //
4 | // Created by Pete Stoppani on 5/19/16.
5 | //
6 |
7 | import Foundation
8 |
9 | /* Format
10 | {
11 | "": {
12 | "params": {"":{"type":""}, ... },
13 | "actions": [, ... ]
14 | }
15 | */
16 |
17 | class SwipeEvent: NSObject {
18 | private let info:[String:Any]
19 | let actions:[SwipeAction]
20 | private(set) lazy var params: [String:Any]? = {
21 | return self.info["params"] as? [String:Any]
22 | }()
23 |
24 | init(type: String, info: [String:Any]) {
25 | self.info = info
26 | if let paramsInfo = info["params"] as? [String:Any] {
27 | NSLog("XdEvent params: \(paramsInfo)")
28 | }
29 | let actionsInfo = info["actions"] as? [[String:Any]] ?? [[String:Any]]()
30 | actions = actionsInfo.map { SwipeAction(info: $0) }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/core/SwipeEventHandler.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwipeEventHandler.swift
3 | //
4 | // Created by Pete Stoppani on 5/19/16.
5 | //
6 |
7 | import Foundation
8 |
9 | class SwipeEventHandler: NSObject {
10 | static var count = 0
11 |
12 | private var events = [String:SwipeEvent]()
13 |
14 | override init () {
15 | super.init()
16 | SwipeEventHandler.count += 1
17 | //print("SEventHandler init", SwipeEventHandler.count)
18 | }
19 |
20 | deinit {
21 | SwipeEventHandler.count -= 1
22 | //print("SEventHandler deinit", SwipeEventHandler.count)
23 | }
24 |
25 | func parse(_ eventsInfo: [String:Any]) {
26 | for eventType in eventsInfo.keys {
27 | //NSLog("XdEventH parsed event: \(eventType)");
28 | if let eventInfo = eventsInfo[eventType] as? [String:Any] {
29 | let event = SwipeEvent(type: eventType, info: eventInfo)
30 | events[eventType] = event
31 | }
32 | }
33 | }
34 |
35 | func actionsFor(_ event: String) -> [SwipeAction]? {
36 | return events[event]?.actions
37 | }
38 |
39 | func getEvent(_ event: String) -> SwipeEvent? {
40 | return events[event]
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/core/SwipeExtensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwipeExtensions.swift
3 | // sample
4 | //
5 | // Created by satoshi on 10/15/15.
6 | // Copyright © 2015 Satoshi Nakajima. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | extension String {
12 | var localized:String {
13 | return NSLocalizedString(self, comment:"")
14 | }
15 | }
16 |
17 | extension URL {
18 | static func url(_ urlString:String, baseURL:URL?) -> URL? {
19 | let url = URL(string: urlString, relativeTo: baseURL)
20 | if let scheme = url?.scheme, scheme.count > 0 {
21 | return url
22 | }
23 |
24 | var components = urlString.components(separatedBy: "/")
25 | if components.count == 1 {
26 | return Bundle.main.url(forResource: urlString, withExtension: nil)
27 | }
28 | let filename = components.last
29 | components.removeLast()
30 | let dir = components.joined(separator: "/")
31 | return Bundle.main.url(forResource: filename, withExtension: nil, subdirectory: dir)
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/core/SwipeHttpGet.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwipeHttpGet.swift
3 | //
4 | // Created by Pete Stoppani on 6/9/16.
5 | //
6 |
7 | import Foundation
8 |
9 | class SwipeHttpGet : SwipeNode {
10 | let TAG = "SwipeHttpGet"
11 | private static var getters = [SwipeHttpGet]()
12 | private var params: [String:Any]?
13 | private var data: [String:Any]?
14 |
15 | static func create(_ parent: SwipeNode, getInfo: [String:Any]) {
16 | let geter = SwipeHttpGet(parent: parent, getInfo: getInfo)
17 | getters.append(geter)
18 | }
19 |
20 | init(parent: SwipeNode, getInfo: [String:Any]) {
21 | super.init(parent: parent)
22 |
23 | if let eventsInfo = getInfo["events"] as? [String:Any] {
24 | eventHandler.parse(eventsInfo)
25 | }
26 |
27 | if let sourceInfo = getInfo["source"] as? [String:Any] {
28 | if let urlString = sourceInfo["url"] as? String, let url = URL(string: urlString) {
29 | SwipeAssetManager.sharedInstance().loadAsset(url, prefix: "", bypassCache:true) { (urlLocal:URL?, error:NSError?) -> Void in
30 | if let urlL = urlLocal, error == nil, let data = try? Data(contentsOf: urlL) {
31 | do {
32 | guard let json = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions()) as? [String:Any] else {
33 | self.handleError("get \(urlString): not a dictionary.")
34 | return
35 | }
36 | // Success
37 | if let event = self.eventHandler.getEvent("completion"), let actionsInfo = self.eventHandler.actionsFor("completion") {
38 | self.data = json
39 | self.params = event.params
40 | self.execute(self, actions: actionsInfo)
41 | }
42 | } catch let error as NSError {
43 | self.handleError("get \(urlString): invalid JSON file \(error.localizedDescription)")
44 | return
45 | }
46 | } else {
47 | self.handleError("get \(urlString): \(error?.localizedDescription ?? "")")
48 | }
49 | }
50 | } else {
51 | self.handleError("get missing or invalid url")
52 | }
53 | } else {
54 | self.handleError("get missing source")
55 | }
56 | }
57 |
58 | private func handleError(_ errorMsg: String) {
59 | if let event = self.eventHandler.getEvent("error"), let actionsInfo = self.eventHandler.actionsFor("error") {
60 | self.data = ["message":errorMsg]
61 | self.params = event.params
62 | self.execute(self, actions: actionsInfo)
63 | } else {
64 | NSLog(TAG + errorMsg)
65 | }
66 | }
67 |
68 | func cancel() {
69 |
70 | }
71 |
72 | static func cancelAll() {
73 | for timer in getters {
74 | timer.cancel()
75 | }
76 |
77 | getters.removeAll()
78 | }
79 |
80 | // SwipeNode
81 |
82 | override func getPropertiesValue(_ originator: SwipeNode, info: [String:Any]) -> Any? {
83 | let prop = info.keys.first!
84 | NSLog(TAG + " getPropsVal(\(prop))")
85 |
86 | switch (prop) {
87 | case "params":
88 | if let params = self.params, let data = self.data {
89 | NSLog(TAG + " not checking params \(params)")
90 | var item:[String:Any] = ["params":data]
91 | var path = info
92 | var property = "params"
93 |
94 | while (true) {
95 | if let next = path[property] as? String {
96 | if let sub = item[property] as? [String:Any] {
97 | return sub[next]
98 | } else {
99 | return nil
100 | }
101 | } else if let next = path[property] as? [String:Any] {
102 | if let sub = item[property] as? [String:Any] {
103 | path = next
104 | property = path.keys.first!
105 | item = sub
106 | } else {
107 | return nil
108 | }
109 | } else {
110 | return nil
111 | }
112 | }
113 |
114 | // loop on properties in info until get to a String
115 | }
116 | break;
117 | default:
118 | return nil
119 | }
120 |
121 | return nil
122 | }
123 |
124 | }
125 |
--------------------------------------------------------------------------------
/core/SwipeHttpPost.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwipeHttpPost.swift
3 | //
4 | // Created by Pete Stoppani on 6/16/16.
5 | //
6 |
7 | import Foundation
8 |
9 | class SwipeHttpPost : SwipeNode {
10 | let TAG = "SWPost"
11 | private static var posters = [SwipeHttpPost]()
12 | private var params: [String:Any]?
13 | private var data: [String:Any]?
14 |
15 | static func create(_ parent: SwipeNode, postInfo: [String:Any]) {
16 | let poster = SwipeHttpPost(parent: parent, postInfo: postInfo)
17 | posters.append(poster)
18 | }
19 |
20 | init(parent: SwipeNode, postInfo: [String:Any]) {
21 | super.init(parent: parent)
22 |
23 | if let eventsInfo = postInfo["events"] as? [String:Any] {
24 | eventHandler.parse(eventsInfo)
25 | }
26 |
27 | if let targetInfo = postInfo["target"] as? [String:Any] {
28 | if var urlString = targetInfo["url"] as? String {
29 | if let params = postInfo["params"] as? [String:Any] {
30 | var paramsSeparator = "?"
31 | if urlString.contains("?") {
32 | paramsSeparator = "&"
33 | }
34 |
35 | for param in params.keys {
36 | var val: String?
37 |
38 | if let str = params[param] as? String {
39 | val = str
40 | } else if let valInfo = params[param] as? [String:Any],
41 | let valOfInfo = valInfo["valueOf"] as? [String:Any],
42 | let str = parent.getValue(parent, info:valOfInfo) as? String {
43 | val = str
44 | }
45 |
46 | if val != nil {
47 | urlString.append(paramsSeparator)
48 | urlString.append(param)
49 | urlString.append("=")
50 | urlString.append(val!.replacingOccurrences(of: "?", with: ""))
51 | paramsSeparator = "&"
52 | }
53 | }
54 | }
55 | if let encoded = urlString.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed),
56 | let url = URL(string: encoded) {
57 | var request = URLRequest(url: url)
58 | request.httpMethod = "POST"
59 |
60 | if let dataStr = postInfo["data"] as? String {
61 | request.httpBody = dataStr.data(using: .utf8)
62 | request.setValue("text/plain; charset=UTF-8", forHTTPHeaderField: "Content-Type")
63 | }
64 | if let data = postInfo["data"] as? [String:Any] {
65 | let evalData = parent.evaluate(data)
66 | do {
67 | request.httpBody = try JSONSerialization.data(withJSONObject: evalData, options: JSONSerialization.WritingOptions.prettyPrinted)
68 | request.setValue("application/json; charset=UTF-8", forHTTPHeaderField: "Content-Type")
69 | } catch let error as NSError {
70 | print("error=\(error)")
71 | self.handleError("post error \(error)")
72 | return
73 | }
74 | }
75 |
76 | if let headers = postInfo["headers"] as? [String:String] {
77 | for h in headers.keys {
78 | request.setValue(headers[h], forHTTPHeaderField: h)
79 | }
80 | }
81 |
82 | let task = URLSession.shared.dataTask(with: request) { data, response, error in
83 | guard error == nil && data != nil else { // check for fundamental networking error
84 | print("error=\(String(describing: error))")
85 | self.handleError("post error \(String(describing: error))")
86 | return
87 | }
88 |
89 | if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode != 200 { // check for http errors
90 | print("statusCode should be 200, but is \(httpStatus.statusCode)")
91 | print("response = \(String(describing: response))")
92 | self.handleError("post error \(httpStatus.statusCode)")
93 | return
94 | }
95 |
96 | let responseString = String(data: data!, encoding: .utf8)
97 | print("responseString = \(String(describing: responseString))")
98 |
99 | do {
100 | guard let json = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions()) as? [String:Any] else {
101 | self.handleError("post \(urlString): not a dictionary.")
102 | return
103 | }
104 | // Success
105 | if let event = self.eventHandler.getEvent("completion"), let actionsInfo = self.eventHandler.actionsFor("completion") {
106 | DispatchQueue.main.async {
107 | self.data = json
108 | self.params = event.params
109 | self.execute(self, actions: actionsInfo)
110 | }
111 | }
112 | } catch let error as NSError {
113 | self.handleError("post \(urlString): invalid JSON file \(error.localizedDescription)")
114 | return
115 | }
116 | }
117 | task.resume()
118 | } else {
119 | self.handleError("post missing or invalid url")
120 | }
121 | } else {
122 | self.handleError("post missing or invalid url")
123 | }
124 | } else {
125 | self.handleError("post missing target")
126 | }
127 | }
128 |
129 | private func handleError(_ errorMsg: String) {
130 | if let event = self.eventHandler.getEvent("error"), let actionsInfo = self.eventHandler.actionsFor("error") {
131 | DispatchQueue.main.async {
132 | self.data = ["message":errorMsg]
133 | self.params = event.params
134 | self.execute(self, actions: actionsInfo)
135 | }
136 | } else {
137 | NSLog(TAG + errorMsg)
138 | }
139 | }
140 |
141 | func cancel() {
142 |
143 | }
144 |
145 | static func cancelAll() {
146 | posters.removeAll()
147 | }
148 |
149 | // SwipeNode
150 |
151 | override func getPropertiesValue(_ originator: SwipeNode, info: [String:Any]) -> Any? {
152 | let prop = info.keys.first!
153 | NSLog(TAG + " getPropsVal(\(prop))")
154 |
155 | switch (prop) {
156 | case "params":
157 | if let params = self.params, let data = self.data {
158 | NSLog(TAG + " not checking params \(params)")
159 | var item:[String:Any] = ["params":data]
160 | var path = info
161 | var property = "params"
162 |
163 | while (true) {
164 | if let next = path[property] as? String {
165 | if let sub = item[property] as? [String:Any] {
166 | let ret = sub[next]
167 | if let str = ret as? String {
168 | return str
169 | } else if let arr = ret as? [Any] {
170 | if arr.count > 0 {
171 | _ = "Array handling needs to be completed"
172 | return arr[0]
173 | } else {
174 | return nil
175 | }
176 | } else {
177 | return ret
178 | }
179 | } else {
180 | return nil
181 | }
182 | } else if let next = path[property] as? [String:Any] {
183 | if let sub = item[property] as? [String:Any] {
184 | path = next
185 | property = path.keys.first!
186 | item = sub
187 | } else {
188 | return nil
189 | }
190 | } else {
191 | return nil
192 | }
193 | }
194 |
195 | // loop on properties in info until get to a String
196 | }
197 | break;
198 | default:
199 | return nil
200 | }
201 |
202 | return nil
203 | }
204 |
205 | }
206 |
--------------------------------------------------------------------------------
/core/SwipeMarkdown.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwipeMarkdown.swift
3 | // Swipe
4 | //
5 | // Created by satoshi on 9/21/15.
6 | // Copyright © 2015 Satoshi Nakajima. All rights reserved.
7 | //
8 | #if os(OSX)
9 | import Cocoa
10 | #else
11 | import UIKit
12 | #endif
13 |
14 | class SwipeMarkdown {
15 | private var attrs = [String:[NSAttributedString.Key:Any]]()
16 | private var prefixes = [
17 | "-":"\u{2022} ", // bullet (U+2022), http://graphemica.com/%E2%80%A2
18 | "```":" ",
19 | ]
20 | private let scale:CGSize
21 | private var shadow:NSShadow?
22 |
23 | func attributesWith(_ fontSize:CGFloat, paragraphSpacing:CGFloat, fontName:String? = nil) -> [NSAttributedString.Key:Any] {
24 | let style = NSMutableParagraphStyle()
25 | style.lineBreakMode = NSLineBreakMode.byWordWrapping
26 | style.paragraphSpacing = paragraphSpacing * scale.height
27 | var font = UIFont.systemFont(ofSize: fontSize * scale.height)
28 | if let name = fontName,
29 | let namedFont = UIFont(name: name, size: fontSize * scale.height) {
30 | font = namedFont
31 | }
32 | var attrs = [NSAttributedString.Key.font: font, NSAttributedString.Key.paragraphStyle: style]
33 | if let shadow = self.shadow {
34 | attrs[NSAttributedString.Key.shadow] = shadow
35 | }
36 | return attrs
37 | }
38 |
39 | // Use function instead of lazy initializer to work around a probable bug in Swift
40 | private func genAttrs() -> [String:[NSAttributedString.Key:Any]] {
41 | return [
42 | "#": self.attributesWith(32, paragraphSpacing: 16),
43 | "##": self.attributesWith(28, paragraphSpacing: 14),
44 | "###": self.attributesWith(24, paragraphSpacing: 12),
45 | "####": self.attributesWith(22, paragraphSpacing: 11),
46 | "*": self.attributesWith(20, paragraphSpacing: 10),
47 | "-": self.attributesWith(20, paragraphSpacing: 5),
48 | "```": self.attributesWith(14, paragraphSpacing: 0, fontName: "Courier"),
49 | "```+": self.attributesWith(7, paragraphSpacing: 0, fontName: "Courier"),
50 | ]
51 | }
52 |
53 | init(info:[String:Any]?, scale:CGSize, dimension:CGSize) {
54 | self.scale = scale
55 | if let params = info {
56 | shadow = SwipeParser.parseShadow(params["shadow"], scale: scale)
57 | }
58 | attrs = genAttrs()
59 |
60 | if let markdownInfo = info,
61 | let styles = markdownInfo["styles"] as? [String:Any] {
62 | for (keyMark, value) in styles {
63 | if let attrInfo = value as? [String:Any] {
64 | var attrCopy:[NSAttributedString.Key:Any]
65 | if let attr = attrs[keyMark] {
66 | attrCopy = attr
67 | } else {
68 | attrCopy = self.attributesWith(20, paragraphSpacing: 10)
69 | }
70 | let styleCopy = NSMutableParagraphStyle()
71 | if let style = attrCopy[NSAttributedString.Key.paragraphStyle] as? NSParagraphStyle {
72 | // WARNING: copy all properties
73 | styleCopy.lineBreakMode = style.lineBreakMode
74 | styleCopy.paragraphSpacing = style.paragraphSpacing
75 | }
76 |
77 | for (keyAttr, attrValue) in attrInfo {
78 | switch(keyAttr) {
79 | case "color":
80 | // the value MUST be UIColor or NSColor, not CGColor
81 | attrCopy[NSAttributedString.Key.foregroundColor] = UIColor(cgColor:SwipeParser.parseColor(attrValue))
82 | case "font":
83 | attrCopy[NSAttributedString.Key.font] = SwipeParser.parseFont(attrValue, scale:scale, full:dimension.height)
84 | case "prefix":
85 | if let prefix = attrValue as? String {
86 | prefixes[keyMark] = prefix
87 | }
88 | case "alignment":
89 | if let alignment = attrValue as? String {
90 | switch(alignment) {
91 | case "center":
92 | styleCopy.alignment = .center
93 | case "right":
94 | styleCopy.alignment = .right
95 | case "left":
96 | styleCopy.alignment = .left
97 | default:
98 | break
99 | }
100 | }
101 | break
102 | /*
103 | // iOS does not allow us to mix multiple shadows
104 | case "shadow":
105 | attr[NSShadowAttributeName] = SwipeParser.parseShadow(attrValue, scale: scale)
106 | */
107 | default:
108 | break;
109 | }
110 | }
111 | attrCopy[NSAttributedString.Key.paragraphStyle] = styleCopy
112 | attrs[keyMark] = attrCopy
113 | }
114 | }
115 | }
116 | }
117 |
118 | func parse(_ markdowns:[String]) -> NSAttributedString {
119 | let strs = NSMutableAttributedString()
120 | var fCode = false
121 | for (index, markdown) in markdowns.enumerated() {
122 | var (key, body):(String?, String) = {
123 | if markdown == "```" {
124 | fCode = !fCode
125 | return fCode ? (nil, "") : ("```+", "")
126 | } else if fCode {
127 | return ("```", markdown)
128 | } else {
129 | for prefix in attrs.keys {
130 | let result = markdown.commonPrefix(with: prefix + " ", options: NSString.CompareOptions.literal)
131 | if result == prefix + " " {
132 | return (prefix, String(markdown[markdown.index(markdown.startIndex, offsetBy: prefix.count + 1)...]))
133 | }
134 | }
135 | }
136 | return ("*", markdown)
137 | }()
138 |
139 | if let keyPrefix = key {
140 | if let prefix = prefixes[keyPrefix] {
141 | body = prefix + body
142 | }
143 | body += ((index < markdowns.count - 1) ? "\n" : "")
144 | strs.append(NSMutableAttributedString(string: body, attributes: attrs[keyPrefix]))
145 | }
146 | }
147 | //NSLog("Markdown:parse \(strs)")
148 | return strs
149 | }
150 | }
151 |
--------------------------------------------------------------------------------
/core/SwipeNode.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwipeNode.swift
3 | //
4 | // Created by Pete Stoppani on 5/19/16.
5 | //
6 |
7 | import Foundation
8 |
9 | class SwipeNode: NSObject {
10 | var children = [SwipeNode]()
11 | private(set) weak var parent:SwipeNode?
12 | let eventHandler = SwipeEventHandler()
13 |
14 | init(parent: SwipeNode? = nil) {
15 | self.parent = parent
16 | super.init()
17 | }
18 |
19 | func evaluate(_ info:[String:Any]) -> [String:Any] {
20 | var result = [String:Any]()
21 |
22 | for k in info.keys {
23 | var val = info[k]
24 | if let valInfo = val as? [String:Any], let valOfInfo = valInfo["valueOf"] as? [String:Any] {
25 | val = getValue(self, info: valOfInfo)
26 | if val == nil {
27 | val = ""
28 | }
29 | }
30 |
31 | result[k] = val
32 | }
33 | return result
34 | }
35 |
36 | func execute(_ originator: SwipeNode, actions:[SwipeAction]?) {
37 | if actions == nil {
38 | return
39 | }
40 | for action in actions! {
41 | executeAction(originator, action: action)
42 | }
43 | }
44 |
45 | func executeAction(_ originator: SwipeNode, action: SwipeAction) {
46 | if let getInfo = action.info["get"] as? [String:Any] {
47 | SwipeHttpGet.create(self, getInfo: getInfo)
48 | } else if let postInfo = action.info["post"] as? [String:Any] {
49 | SwipeHttpPost.create(self, postInfo: postInfo)
50 | } else if let timerInfo = action.info["timer"] as? [String:Any] {
51 | SwipeTimer.create(self, timerInfo: timerInfo)
52 | } else {
53 | parent?.executeAction(originator, action: action)
54 | }
55 | }
56 |
57 | func getPropertyValue(_ originator: SwipeNode, property: String) -> Any? {
58 | return self.parent?.getPropertyValue(originator, property: property)
59 | }
60 |
61 | func getPropertiesValue(_ originator: SwipeNode, info: [String:Any]) -> Any? {
62 | return self.parent?.getPropertiesValue(originator, info: info)
63 | }
64 |
65 | func getValue(_ originator: SwipeNode, info: [String:Any]) -> Any? {
66 | var up = true
67 | if let val = info["search"] as? String {
68 | up = val != "children"
69 | }
70 |
71 | if let property = info["property"] as? String {
72 | return getPropertyValue(originator, property: property)
73 | } else if let propertyInfo = info["property"] as? [String:Any] {
74 | return getPropertiesValue(originator, info: propertyInfo)
75 | } else {
76 | if up {
77 | return self.parent?.getValue(originator, info: info)
78 | } else {
79 | for c in self.children {
80 | let val = c.getValue(originator, info: info)
81 | if val != nil {
82 | return val
83 | }
84 | }
85 | }
86 | }
87 |
88 | return nil
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/core/SwipePageTemplate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwipePageTemplate.swift
3 | // Swipe
4 | //
5 | // Created by satoshi on 9/8/15.
6 | // Copyright (c) 2015 Satoshi Nakajima. All rights reserved.
7 | //
8 |
9 | #if os(OSX)
10 | import Cocoa
11 | //public typealias UIColor = NSColor
12 | //public typealias UIFont = NSFont
13 | #else
14 | import UIKit
15 | #endif
16 | import AVFoundation
17 |
18 | private func MyLog(_ text:String, level:Int = 0) {
19 | let s_verbosLevel = 0
20 | if level <= s_verbosLevel {
21 | NSLog(text)
22 | }
23 | }
24 |
25 | class SwipePageTemplate: NSObject, AVAudioPlayerDelegate {
26 | let pageTemplateInfo:[String:Any]
27 | private let baseURL:URL?
28 | private let name:String
29 | private var bgmPlayer:AVAudioPlayer?
30 | private var fDebugEntered = false
31 |
32 | lazy var resourceURLs:[URL:String] = {
33 | var urls = [URL:String]()
34 | if let value = self.pageTemplateInfo["bgm"] as? String,
35 | let url = URL.url(value, baseURL: self.baseURL) {
36 | urls[url] = ""
37 | }
38 | return urls
39 | }()
40 |
41 | init(name:String, info:[String:Any], baseURL:URL?) {
42 | self.baseURL = baseURL
43 | self.name = name
44 | self.pageTemplateInfo = info
45 | }
46 |
47 | // This function is called when a page associated with this pageTemplate is activated (entered)
48 | // AND the previous page is NOT associated with this pageTemplate object.
49 | func didEnter(_ prefetcher:SwipePrefetcher) {
50 | assert(fDebugEntered == false, "re-entering")
51 | fDebugEntered = true
52 |
53 | if let value = self.pageTemplateInfo["bgm"] as? String,
54 | let urlRaw = URL.url(value, baseURL: baseURL),
55 | let url = prefetcher.map(urlRaw) {
56 | MyLog("SWPageTemplate didEnter with bgm=\(value)", level:1)
57 | SwipeAssetManager.sharedInstance().loadAsset(url, prefix: "", bypassCache:false, callback: { (urlLocal:URL?, _) -> Void in
58 | if self.fDebugEntered,
59 | let urlL = urlLocal,
60 | let player = try? AVAudioPlayer(contentsOf: urlL) {
61 | player.delegate = self
62 | player.play()
63 | self.bgmPlayer = player
64 | }
65 | })
66 | } else {
67 | //NSLog("SWPageTemplate didEnter failed to create URL")
68 | }
69 | }
70 |
71 | // This function is called when a page associated with this pageTemplate is deactivated (leaved)
72 | // AND the subsequent page is not associated with this pageTemplate object.
73 | func didLeave() {
74 | assert(fDebugEntered == true, "leaving without entering")
75 | fDebugEntered = false
76 |
77 | if let player = bgmPlayer {
78 | player.stop()
79 | player.delegate = nil
80 | bgmPlayer = nil
81 | }
82 | }
83 |
84 | // We repeat the bgm
85 | func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
86 | if let player = bgmPlayer {
87 | if flag {
88 | player.play()
89 | }
90 | }
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/core/SwipeSynthesizer.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwipeSynthesizer.swift
3 | // sample
4 | //
5 | // Created by satoshi on 9/21/15.
6 | // Copyright © 2015 Satoshi Nakajima. All rights reserved.
7 | //
8 |
9 | #if os(OSX)
10 | import Cocoa
11 | public typealias AVSpeechSynthesizer = NSSpeechSynthesizer
12 | #else
13 | import UIKit
14 | #endif
15 |
16 | import AVFoundation
17 |
18 | class SwipeSymthesizer: NSObject {
19 | private static let singleton = SwipeSymthesizer()
20 | let synth = AVSpeechSynthesizer()
21 |
22 | static func sharedInstance() -> SwipeSymthesizer {
23 | return SwipeSymthesizer.singleton
24 | }
25 |
26 | // method
27 | func synthesizer() -> AVSpeechSynthesizer {
28 | return self.synth
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/core/SwipeTextArea.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwipeTextArea.swift
3 | //
4 | // Created by Pete Stoppani on 6/7/16.
5 | //
6 |
7 | import Foundation
8 |
9 | #if os(OSX)
10 | import Cocoa
11 | #else
12 | import UIKit
13 | #endif
14 |
15 | class SwipeTextArea: SwipeView, UITextViewDelegate {
16 | private var screenDimension = CGSize(width: 0, height: 0)
17 | private var textView: UITextView
18 |
19 | init(parent: SwipeNode, info: [String:Any], frame: CGRect, screenDimension: CGSize) {
20 | self.screenDimension = screenDimension
21 | self.textView = UITextView(frame: frame)
22 | super.init(parent: parent, info: info)
23 | self.textView.delegate = self
24 | self.textView.backgroundColor = UIColor.clear
25 | //self.textView.becomeFirstResponder()
26 | self.view = self.textView as UIView
27 | }
28 |
29 | override func setText(_ text:String, scale:CGSize, info:[String:Any], dimension:CGSize, layer:CALayer?) -> Bool {
30 | self.textView.text = text
31 | self.textView.textAlignment = NSTextAlignment.center
32 |
33 | func processAlignment(_ alignment:String) {
34 | switch(alignment) {
35 | case "center":
36 | self.textView.textAlignment = .center
37 | case "left":
38 | self.textView.textAlignment = .left
39 | case "right":
40 | self.textView.textAlignment = .right
41 | case "justified":
42 | self.textView.textAlignment = .justified
43 | default:
44 | break
45 | }
46 | }
47 | if let alignment = info["textAlign"] as? String {
48 | processAlignment(alignment)
49 | } else if let alignments = info["textAlign"] as? [String] {
50 | for alignment in alignments {
51 | processAlignment(alignment)
52 | }
53 | }
54 | let fontSize:CGFloat = {
55 | var ret = 20.0 / 480.0 * dimension.height // default
56 | if let fontSize = info["fontSize"] as? CGFloat {
57 | ret = fontSize
58 | } else if let fontSize = info["fontSize"] as? String {
59 | ret = SwipeParser.parsePercent(fontSize, full: dimension.height, defaultValue: ret)
60 | }
61 | return round(ret * scale.height)
62 | }()
63 |
64 | self.textView.font = UIFont(name: "Helvetica", size: fontSize)
65 | self.textView.textColor = UIColor(cgColor: SwipeParser.parseColor(info["textColor"], defaultColor: UIColor.black.cgColor))
66 |
67 | parent!.execute(self, actions: parent!.eventHandler.actionsFor("textChanged"))
68 |
69 | return true
70 | }
71 |
72 | override func getPropertyValue(_ originator: SwipeNode, property: String) -> Any? {
73 | switch (property) {
74 | case "text":
75 | return self.textView.text
76 | case "text.length":
77 | return self.textView.text?.count
78 | default:
79 | return super.getPropertyValue(originator, property: property)
80 | }
81 | }
82 |
83 | override func isFirstResponder() -> Bool {
84 | return view!.isFirstResponder
85 | }
86 |
87 | // UITextViewDelegate
88 |
89 | func textViewDidChange(_ textView: UITextView) {
90 | parent!.execute(self, actions: parent!.eventHandler.actionsFor("textChanged"))
91 | }
92 |
93 | func textViewDidBeginEditing(_ textView: UITextView) {
94 | }
95 |
96 | func textViewDidEndEditing(_ textView: UITextView) {
97 | parent!.execute(self, actions: parent!.eventHandler.actionsFor("endEdit"))
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/core/SwipeTextField.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwipeTextField.swift
3 | //
4 | // Created by Pete Stoppani on 8/24/16.
5 | //
6 |
7 | import Foundation
8 |
9 | #if os(OSX)
10 | import Cocoa
11 | #else
12 | import UIKit
13 | #endif
14 |
15 | class SwipeTextField: SwipeView, UITextFieldDelegate {
16 | private var screenDimension = CGSize(width: 0, height: 0)
17 |
18 | class InternalTextField: UITextField {
19 | weak var wrapper: SwipeTextField?
20 |
21 | init(wrapper: SwipeTextField?, frame: CGRect) {
22 | super.init(frame: frame)
23 | self.wrapper = wrapper
24 | }
25 |
26 | required init?(coder aDecoder: NSCoder) {
27 | fatalError("init(coder:) has not been implemented")
28 | }
29 |
30 | override var canBecomeFocused: Bool {
31 | if let wrapper = self.wrapper, let parent = wrapper.parent as? SwipeView {
32 | return parent.fFocusable
33 | } else {
34 | return super.canBecomeFocused
35 | }
36 | }
37 |
38 | override func didUpdateFocus(in context: UIFocusUpdateContext, with coordinator: UIFocusAnimationCoordinator) {
39 | if let wrapper = self.wrapper, let parent = wrapper.parent as? SwipeView {
40 | // lostFocus must be fired before gainedFocus
41 | if self == context.previouslyFocusedView {
42 | if let actions = parent.eventHandler.actionsFor("lostFocus") {
43 | parent.execute(parent, actions: actions)
44 | }
45 | }
46 | if self == context.nextFocusedView {
47 | if let actions = parent.eventHandler.actionsFor("gainedFocus") {
48 | parent.execute(parent, actions: actions)
49 | }
50 | }
51 | } else {
52 | super.didUpdateFocus(in: context, with: coordinator)
53 | }
54 | }
55 | }
56 |
57 | var textView: InternalTextField?
58 |
59 | init(parent: SwipeView, info: [String:Any], frame: CGRect, screenDimension: CGSize) {
60 | self.screenDimension = screenDimension
61 | super.init(parent: parent, info: info)
62 | self.textView = InternalTextField(wrapper: self, frame: frame)
63 | self.textView!.delegate = self
64 | self.textView!.backgroundColor = UIColor.clear
65 | self.view = self.textView! as UIView
66 | }
67 |
68 | override func setText(_ text:String, scale:CGSize, info:[String:Any], dimension:CGSize, layer:CALayer?) -> Bool {
69 | if let textView = self.textView {
70 | textView.text = text
71 | textView.textAlignment = NSTextAlignment.center
72 |
73 | func processAlignment(_ alignment:String) {
74 | switch(alignment) {
75 | case "center":
76 | textView.textAlignment = .center
77 | case "left":
78 | textView.textAlignment = .left
79 | case "right":
80 | textView.textAlignment = .right
81 | case "justified":
82 | textView.textAlignment = .justified
83 | default:
84 | break
85 | }
86 | }
87 | if let alignment = info["textAlign"] as? String {
88 | processAlignment(alignment)
89 | } else if let alignments = info["textAlign"] as? [String] {
90 | for alignment in alignments {
91 | processAlignment(alignment)
92 | }
93 | }
94 | let fontSize:CGFloat = {
95 | var ret = 20.0 / 480.0 * dimension.height // default
96 | if let fontSize = info["fontSize"] as? CGFloat {
97 | ret = fontSize
98 | } else if let fontSize = info["fontSize"] as? String {
99 | ret = SwipeParser.parsePercent(fontSize, full: dimension.height, defaultValue: ret)
100 | }
101 | return round(ret * scale.height)
102 | }()
103 |
104 | textView.font = UIFont(name: "Helvetica", size: fontSize)
105 | textView.textColor = UIColor(cgColor: SwipeParser.parseColor(info["textColor"], defaultColor: UIColor.black.cgColor))
106 |
107 | parent!.execute(self, actions: parent!.eventHandler.actionsFor("textChanged"))
108 | }
109 |
110 | return true
111 | }
112 |
113 | override func getPropertyValue(_ originator: SwipeNode, property: String) -> Any? {
114 | switch (property) {
115 | case "text":
116 | return self.textView!.text
117 | case "text.length":
118 | return self.textView?.text?.count
119 | default:
120 | return super.getPropertyValue(originator, property: property)
121 | }
122 | }
123 |
124 | override func isFirstResponder() -> Bool {
125 | return view!.isFirstResponder
126 | }
127 |
128 | // UITextViewDelegate
129 |
130 | func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
131 | DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.01) {
132 | self.parent!.execute(self, actions: self.parent!.eventHandler.actionsFor("textChanged"))
133 | }
134 | return true
135 | }
136 |
137 | func textFieldDidBeginEditing(_ textField: UITextField) {
138 | }
139 |
140 | func textFieldDidEndEditing(_ textField: UITextField) {
141 | parent!.execute(self, actions: parent!.eventHandler.actionsFor("endEdit"))
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/core/SwipeTimer.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwipeTimer.swift
3 | //
4 | // Created by Pete Stoppani on 5/23/16.
5 | //
6 |
7 | import Foundation
8 |
9 | class SwipeTimer : SwipeNode {
10 | static var timers = [SwipeTimer]()
11 | var timer: Timer?
12 | var repeats = false
13 |
14 | static func create(_ parent: SwipeNode, timerInfo: [String:Any]) {
15 | let timer = SwipeTimer(parent: parent, timerInfo: timerInfo)
16 | timers.append(timer)
17 | }
18 |
19 | init(parent: SwipeNode, timerInfo: [String:Any]) {
20 | super.init(parent: parent)
21 | var duration = 0.2
22 | if let value = timerInfo["duration"] as? Double {
23 | duration = value
24 | }
25 | if let value = timerInfo["repeats"] as? Bool {
26 | repeats = value
27 | }
28 | if let eventsInfo = timerInfo["events"] as? [String:Any] {
29 | eventHandler.parse(eventsInfo)
30 |
31 | self.timer = Timer.scheduledTimer(timeInterval: duration, target:self, selector: #selector(SwipeTimer.didTimerTick(_:)), userInfo: nil, repeats: repeats)
32 | }
33 | }
34 |
35 | func cancel() {
36 | self.timer?.invalidate()
37 | self.timer = nil
38 | }
39 |
40 | static func cancelAll() {
41 | for timer in timers {
42 | timer.cancel()
43 | }
44 |
45 | timers.removeAll()
46 | }
47 |
48 | @objc func didTimerTick(_ timer: Timer) {
49 | if !timer.isValid {
50 | return
51 | }
52 |
53 | if let actions = eventHandler.actionsFor("tick") {
54 | execute(self, actions:actions)
55 | }
56 |
57 | if !repeats {
58 | cancel()
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/core/SwipeView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwipeView.swift
3 | //
4 | // Created by Pete Stoppani on 5/19/16.
5 | //
6 |
7 | import Foundation
8 | #if os(OSX)
9 | import Cocoa
10 | public typealias UIView = NSView
11 | public typealias UIButton = NSButton
12 | public typealias UIScreen = NSScreen
13 | #else
14 | import UIKit
15 | #endif
16 |
17 | protocol SwipeViewDelegate: NSObjectProtocol {
18 | func addedResourceURLs(_ urls:[URL:String], callback:@escaping () -> Void)
19 | }
20 |
21 | class SwipeView: SwipeNode {
22 |
23 | internal var info = [String:Any]()
24 | internal var fEnabled = true
25 | internal var fFocusable = false
26 |
27 | class InternalView: UIView {
28 | weak var wrapper: SwipeView?
29 |
30 | init(wrapper: SwipeView?, frame: CGRect) {
31 | super.init(frame: frame)
32 | self.wrapper = wrapper
33 | }
34 |
35 | required init?(coder aDecoder: NSCoder) {
36 | fatalError("init(coder:) has not been implemented")
37 | }
38 |
39 | override var canBecomeFocused: Bool {
40 | if let element = self.wrapper as? SwipeElement, let _ = element.helper?.view {
41 | return false
42 | } else if let wrapper = self.wrapper {
43 | return wrapper.fFocusable
44 | } else {
45 | return super.canBecomeFocused
46 | }
47 | }
48 |
49 | override func didUpdateFocus(in context: UIFocusUpdateContext, with coordinator: UIFocusAnimationCoordinator) {
50 | if let wrapper = self.wrapper {
51 | // lostFocus must be fired before gainedFocus
52 | if let actions = wrapper.eventHandler.actionsFor("lostFocus"), self == context.previouslyFocusedView {
53 | wrapper.execute(wrapper, actions: actions)
54 | }
55 | if let actions = wrapper.eventHandler.actionsFor("gainedFocus"), self == context.nextFocusedView {
56 | wrapper.execute(wrapper, actions: actions)
57 | }
58 | } else {
59 | super.didUpdateFocus(in: context, with: coordinator)
60 | }
61 | }
62 | }
63 | var view: UIView?
64 |
65 | init(info: [String:Any]) {
66 | self.info = info
67 | super.init()
68 | }
69 |
70 | init(parent: SwipeNode, info: [String:Any]) {
71 | self.info = info
72 | super.init(parent: parent)
73 | }
74 |
75 | func setupGestureRecognizers() {
76 | var doubleTapRecognizer: UITapGestureRecognizer?
77 |
78 | if eventHandler.actionsFor("doubleTapped") != nil {
79 | doubleTapRecognizer = UITapGestureRecognizer(target: self, action:#selector(SwipeView.didDoubleTap(_:)))
80 | doubleTapRecognizer!.numberOfTapsRequired = 2
81 | doubleTapRecognizer!.cancelsTouchesInView = false
82 | view!.addGestureRecognizer(doubleTapRecognizer!)
83 | }
84 |
85 | let tapRecognizer = UITapGestureRecognizer(target: self, action:#selector(SwipeView.didTap(_:)))
86 | if doubleTapRecognizer != nil {
87 | tapRecognizer.require(toFail: doubleTapRecognizer!)
88 | }
89 | tapRecognizer.cancelsTouchesInView = false
90 | view!.addGestureRecognizer(tapRecognizer)
91 | }
92 |
93 | lazy var name:String = {
94 | if let value = self.info["id"] as? String {
95 | return value
96 | }
97 | return "" // default
98 | }()
99 |
100 | lazy var data:Any = {
101 | if let value = self.info["data"] as? String {
102 | return value
103 | } else if let value = self.info["data"] as? [String:Any] {
104 | return value
105 | }
106 | return "" // default
107 | }()
108 |
109 | func endEditing() {
110 | if let view = self.view {
111 | let ended = view.endEditing(true)
112 | if !ended {
113 | if let p = self.parent as? SwipeView {
114 | p.endEditing()
115 | }
116 | }
117 | }
118 | }
119 |
120 | func setText(_ text:String, scale:CGSize, info:[String:Any], dimension:CGSize, layer:CALayer?) -> Bool {
121 | return false
122 | }
123 |
124 | func tapped() {
125 | if let p = self.parent as? SwipeView {
126 | p.tapped()
127 | }
128 | }
129 |
130 | private func completeTap() {
131 | endEditing()
132 | tapped()
133 | }
134 |
135 | @objc func didTap(_ recognizer: UITapGestureRecognizer) {
136 | if let actions = eventHandler.actionsFor("tapped"), fEnabled {
137 | execute(self, actions: actions)
138 | completeTap()
139 | } else if let p = self.parent as? SwipeView {
140 | p.didTap(recognizer)
141 | // parent will completeTap()
142 | } else {
143 | completeTap()
144 | }
145 | }
146 |
147 | @objc func didDoubleTap(_ recognizer: UITapGestureRecognizer) {
148 | if let actions = eventHandler.actionsFor("doubleTapped"), fEnabled {
149 | execute(self, actions: actions)
150 | }
151 | }
152 |
153 | override func executeAction(_ originator: SwipeNode, action: SwipeAction) {
154 | if let updateInfo = action.info["update"] as? [String:Any] {
155 | var name = "*"; // default is 'self'
156 | if let value = updateInfo["id"] as? String {
157 | name = value
158 | }
159 | var up = true
160 | if let value = updateInfo["search"] as? String {
161 | up = value != "children"
162 | }
163 | _ = updateElement(originator, name:name, up:up, info: updateInfo)
164 | } else if let appendInfo = action.info["append"] as? [String:Any] {
165 | var name = "*"; // default is 'self'
166 | if let value = appendInfo["id"] as? String {
167 | name = value
168 | }
169 | var up = true
170 | if let value = appendInfo["search"] as? String {
171 | up = value != "children"
172 | }
173 | _ = appendList(originator, name:name, up:up, info: appendInfo)
174 | } else {
175 | super.executeAction(originator, action: action)
176 | }
177 | }
178 |
179 | func updateElement(_ originator: SwipeNode, name: String, up: Bool, info: [String:Any]) -> Bool {
180 | return false
181 | }
182 |
183 | override func getPropertyValue(_ originator: SwipeNode, property: String) -> Any? {
184 | switch (property) {
185 | case "data":
186 | return self.data
187 | case "screenX":
188 | return self.view!.superview?.convert(self.view!.frame.origin, to: nil).x
189 | case "screenY":
190 | return self.view!.superview?.convert(self.view!.frame.origin, to: nil).y
191 | case "x":
192 | return self.view!.frame.origin.x
193 | case "y":
194 | return self.view!.frame.origin.y
195 | case "w":
196 | return self.view!.frame.size.width
197 | case "h":
198 | return self.view!.frame.size.height
199 | default:
200 | return nil
201 | }
202 | }
203 | func appendList(_ originator: SwipeNode, name: String, up: Bool, info: [String:Any]) -> Bool {
204 | return false
205 | }
206 |
207 | func appendList(_ originator: SwipeNode, info: [String:Any]) {
208 | }
209 |
210 | func isFirstResponder() -> Bool {
211 | return false
212 | }
213 |
214 | func findFirstResponder() -> SwipeView? {
215 | if self.isFirstResponder() {
216 | return self
217 | }
218 |
219 | for c in self.children {
220 | if let e = c as? SwipeElement {
221 | if let fr = e.findFirstResponder() {
222 | return fr
223 | }
224 | }
225 | }
226 |
227 | return nil;
228 | }
229 |
230 | }
231 |
--------------------------------------------------------------------------------
/network/SNNotificationManager.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SNNotificationManager.swift
3 | //
4 | // Created by satoshi on 3/30/15.
5 | // Copyright (c) 2015 Satoshi Nakajima. All rights reserved.
6 | //
7 |
8 | #if os(OSX)
9 | import Cocoa
10 | #else
11 | import UIKit
12 | #endif
13 |
14 | class SNNotificationManager {
15 | var observers = [NSObjectProtocol]()
16 |
17 | deinit {
18 | clear()
19 | }
20 |
21 | func addObserver(forName name: Notification.Name?, object obj: Any?, queue: OperationQueue?, using block: @escaping (Notification) -> Void) {
22 | let observer = NotificationCenter.default.addObserver(forName: name, object: obj, queue: queue, using: block)
23 | observers.append(observer)
24 | }
25 |
26 | func clear() {
27 | for observer in observers {
28 | NotificationCenter.default.removeObserver(observer)
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/network/SwipeConnection.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwipeConnection.swift
3 | // sample
4 | //
5 | // Created by satoshi on 10/9/15.
6 | // Copyright © 2015 Satoshi Nakajima. All rights reserved.
7 | //
8 |
9 | #if os(OSX)
10 | import Cocoa
11 | #else
12 | import UIKit
13 | #endif
14 |
15 | import CoreData
16 |
17 | private func MyLog(_ text:String, level:Int = 0) {
18 | let s_verbosLevel = 0
19 | if level <= s_verbosLevel {
20 | NSLog(text)
21 | }
22 | }
23 |
24 | class SwipeConnection: NSObject {
25 | private static var connections = [URL:SwipeConnection]()
26 | static let session:URLSession = {
27 | let config = URLSessionConfiguration.default
28 | config.urlCache = nil // disable cache by URLSession (because we do)
29 | return URLSession(configuration: config, delegate: nil, delegateQueue: OperationQueue.main)
30 | }()
31 | static func connection(_ url:URL, urlLocal:URL, entity:NSManagedObject) -> SwipeConnection {
32 | if let connection = connections[url] {
33 | return connection
34 | }
35 | let connection = SwipeConnection(url: url, urlLocal: urlLocal, entity:entity)
36 | //connection.start()
37 | let session = SwipeConnection.session
38 | let start = Date()
39 | let task = session.downloadTask(with: url) { (urlTemp:URL?, res:URLResponse?, error:Swift.Error?) -> Void in
40 | assert(Thread.current == Thread.main, "thread error")
41 | let duration = Date().timeIntervalSince(start)
42 | if let urlT = urlTemp {
43 | if let httpRes = res as? HTTPURLResponse {
44 | if httpRes.statusCode == 200 {
45 | let fm = FileManager.default
46 | do {
47 | let attr = try fm.attributesOfItem(atPath: urlT.path)
48 | if let size = attr[FileAttributeKey.size] as? Int {
49 | connection.fileSize = size
50 | SwipeAssetManager.sharedInstance().wasFileLoaded(connection)
51 | }
52 | } catch {
53 | MyLog("SWConn failed to get attributes (but ignored)")
54 | }
55 | MyLog("SWConn loaded \(url.lastPathComponent) in \(duration)s (\(connection.fileSize))", level:1)
56 | do {
57 | if fm.fileExists(atPath: urlLocal.path) {
58 | try fm.removeItem(at: urlLocal)
59 | }
60 | try fm.copyItem(at: urlT, to: urlLocal)
61 | } catch {
62 | connection.callbackAll(error as NSError)
63 | return
64 | }
65 | } else {
66 | MyLog("SWConn HTTP error (\(url.lastPathComponent), \(httpRes.statusCode))")
67 | connection.callbackAll(NSError(domain: NSURLErrorDomain, code: httpRes.statusCode, userInfo: nil))
68 | return
69 | }
70 | } else {
71 | MyLog("SWConn no HTTPURLResponse, something is wrong!")
72 | }
73 | } else {
74 | MyLog("SWConn network error (\(url.lastPathComponent), \(String(describing: error)))")
75 | }
76 | connection.callbackAll(error as NSError?)
77 | }
78 | task.resume()
79 | return connection
80 | }
81 | let url, urlLocal:URL
82 | let entity:NSManagedObject
83 | var callbacks = Array<(NSError?) -> Void>()
84 | var fileSize = 0
85 |
86 | private init(url:URL, urlLocal:URL, entity:NSManagedObject) {
87 | self.url = url
88 | self.urlLocal = urlLocal
89 | self.entity = entity
90 | super.init()
91 | SwipeConnection.connections[url] = self
92 | }
93 | deinit {
94 | //MyLog("SWCon deinit \(url.lastPathComponent)")
95 | }
96 |
97 | func load(_ callback:@escaping (NSError?) -> Void) {
98 | callbacks.append(callback)
99 | }
100 |
101 | func callbackAll(_ error: NSError?) {
102 | SwipeConnection.connections.removeValue(forKey: self.url)
103 | for callback in callbacks {
104 | callback(error)
105 | }
106 | }
107 |
108 | }
109 |
--------------------------------------------------------------------------------
/network/SwipePrefetcher.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwipePrefetcher.swift
3 | // sample
4 | //
5 | // Created by satoshi on 10/12/15.
6 | // Copyright © 2015 Satoshi Nakajima. All rights reserved.
7 | //
8 |
9 | #if os(OSX)
10 | import Cocoa
11 | #else
12 | import UIKit
13 | #endif
14 |
15 |
16 | private func MyLog(_ text:String, level:Int = 0) {
17 | let s_verbosLevel = 0
18 | if level <= s_verbosLevel {
19 | NSLog(text)
20 | }
21 | }
22 |
23 | class SwipePrefetcher {
24 | private var urls = [URL:String]()
25 | private var urlsFetching = [URL]()
26 | private var urlsFetched = [URL:URL]()
27 | private var urlsFailed = [URL]()
28 | private var errors = [NSError]()
29 | private var fComplete = false
30 | private var _progress = Float(0)
31 |
32 | var progress:Float {
33 | return _progress
34 | }
35 |
36 | init(urls:[URL:String]) {
37 | self.urls = urls
38 | }
39 |
40 | func start(_ callback:@escaping (Bool, [URL], [NSError]) -> Void) {
41 | if fComplete {
42 | MyLog("SWPrefe already completed", level:1)
43 | callback(true, self.urlsFailed, self.errors)
44 | return
45 | }
46 |
47 | let manager = SwipeAssetManager.sharedInstance()
48 | var count = 0
49 | _progress = 0
50 | let fileManager = FileManager.default
51 | for (url,prefix) in urls {
52 | if url.scheme == "file" {
53 | if fileManager.fileExists(atPath: url.path) {
54 | urlsFetched[url] = url
55 | } else {
56 | // On-demand resource support
57 | urlsFetched[url] = Bundle.main.url(forResource: url.lastPathComponent, withExtension: nil)
58 | MyLog("SWPrefe onDemand resource at \(String(describing:urlsFetched[url])) instead of \(url)", level:1)
59 | }
60 | } else {
61 | count += 1
62 | urlsFetching.append(url)
63 | manager.loadAsset(url, prefix: prefix, bypassCache:false, callback: { (urlLocal:URL?, error:NSError?) -> Void in
64 | if let urlL = urlLocal {
65 | self.urlsFetched[url] = urlL
66 | } else {
67 | self.urlsFailed.append(url)
68 | if let error = error {
69 | self.errors.append(error)
70 | }
71 | }
72 | count -= 1
73 | if (count == 0) {
74 | self.fComplete = true
75 | self._progress = 1
76 | MyLog("SWPrefe completed \(self.urlsFetched.count)", level: 1)
77 | callback(true, self.urlsFailed, self.errors)
78 | } else {
79 | self._progress = Float(self.urls.count - count) / Float(self.urls.count)
80 | callback(false, self.urlsFailed, self.errors)
81 | }
82 | })
83 | }
84 | }
85 | if count == 0 {
86 | self.fComplete = true
87 | self._progress = 1
88 | callback(true, urlsFailed, errors)
89 | }
90 | }
91 |
92 | func append(_ urls:[URL:String], callback:@escaping (Bool, [URL], [NSError]) -> Void) {
93 | let manager = SwipeAssetManager.sharedInstance()
94 | var count = 0
95 | _progress = 0
96 | let fileManager = FileManager.default
97 | for (url,prefix) in urls {
98 | self.urls[url] = prefix
99 | if url.scheme == "file" {
100 | if fileManager.fileExists(atPath: url.path) {
101 | urlsFetched[url] = url
102 | } else {
103 | // On-demand resource support
104 | urlsFetched[url] = Bundle.main.url(forResource: url.lastPathComponent, withExtension: nil)
105 | MyLog("SWPrefe onDemand resource at \(String(describing: urlsFetched[url])) instead of \(url)", level:1)
106 | }
107 | } else {
108 | count += 1
109 | urlsFetching.append(url)
110 | manager.loadAsset(url, prefix: prefix, bypassCache:false, callback: { (urlLocal:URL?, error:NSError?) -> Void in
111 | if let urlL = urlLocal {
112 | self.urlsFetched[url] = urlL
113 | } else if let error = error {
114 | self.urlsFailed.append(url)
115 | self.errors.append(error)
116 | }
117 | count -= 1
118 | if (count == 0) {
119 | self.fComplete = true
120 | self._progress = 1
121 | MyLog("SWPrefe completed \(self.urlsFetched.count)", level: 1)
122 | callback(true, self.urlsFailed, self.errors)
123 | } else {
124 | self._progress = Float(self.urls.count - count) / Float(self.urls.count)
125 | callback(false, self.urlsFailed, self.errors)
126 | }
127 | })
128 | }
129 | }
130 | if count == 0 {
131 | self.fComplete = true
132 | self._progress = 1
133 | callback(true, urlsFailed, errors)
134 | }
135 | }
136 | func map(_ url:URL) -> URL? {
137 | return urlsFetched[url]
138 | }
139 |
140 | static func extensionForType(_ memeType:String) -> String {
141 | let ext:String
142 | if memeType == "video/quicktime" {
143 | ext = ".mov"
144 | } else if memeType == "video/mp4" {
145 | ext = ".mp4"
146 | } else {
147 | ext = ""
148 | }
149 | return ext
150 | }
151 |
152 | static func isMovie(_ mimeType:String) -> Bool {
153 | return mimeType == "video/quicktime" || mimeType == "video/mp4"
154 | }
155 | }
156 |
--------------------------------------------------------------------------------
/network/asset.xcdatamodeld/asset.xcdatamodel/contents:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/sample/bug_repro/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // bug_repro
4 | //
5 | // Created by satoshi on 8/23/16.
6 | // Copyright © 2016 Satoshi Nakajima. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 | var window: UIWindow?
15 |
16 |
17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
18 | // Override point for customization after application launch.
19 | return true
20 | }
21 |
22 | func applicationWillResignActive(_ application: UIApplication) {
23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
24 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
25 | }
26 |
27 | func applicationDidEnterBackground(_ application: UIApplication) {
28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
30 | }
31 |
32 | func applicationWillEnterForeground(_ application: UIApplication) {
33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
34 | }
35 |
36 | func applicationDidBecomeActive(_ application: UIApplication) {
37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
38 | }
39 |
40 | func applicationWillTerminate(_ _ application: UIApplication) {
41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
42 | }
43 |
44 |
45 | }
46 |
47 |
--------------------------------------------------------------------------------
/sample/bug_repro/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "29x29",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "40x40",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "40x40",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "60x60",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "60x60",
31 | "scale" : "3x"
32 | }
33 | ],
34 | "info" : {
35 | "version" : 1,
36 | "author" : "xcode"
37 | }
38 | }
--------------------------------------------------------------------------------
/sample/bug_repro/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/sample/bug_repro/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/sample/bug_repro/IMG_9401.mov:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/swipe-org/swipe/3529d5a1fa808c0885942e521798895bcf6199b7/sample/bug_repro/IMG_9401.mov
--------------------------------------------------------------------------------
/sample/bug_repro/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UIRequiredDeviceCapabilities
30 |
31 | armv7
32 |
33 | UISupportedInterfaceOrientations
34 |
35 | UIInterfaceOrientationPortrait
36 | UIInterfaceOrientationLandscapeLeft
37 | UIInterfaceOrientationLandscapeRight
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/sample/bug_repro/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // bug_repro
4 | //
5 | // Created by satoshi on 8/23/16.
6 | // Copyright © 2016 Satoshi Nakajima. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import AVFoundation
11 |
12 | //
13 | // I reported this bug to Apple on August 25, 2016. Bug# 27982201.
14 | //
15 | class ViewController: UIViewController {
16 | @IBOutlet var viewMain:UIView!
17 |
18 | override func viewDidLoad() {
19 | super.viewDidLoad()
20 |
21 | if let urlVideo = Bundle.main.url(forResource: "IMG_9401", withExtension: "mov") {
22 | let videoPlayer = AVPlayer(playerItem: AVPlayerItem(url: urlVideo))
23 | let videoLayer = AVPlayerLayer(player: videoPlayer)
24 | videoLayer.frame = viewMain.bounds
25 | viewMain.layer.addSublayer(videoLayer)
26 | videoPlayer.play()
27 | }
28 | }
29 |
30 | @IBAction func test() {
31 | UIGraphicsBeginImageContext(view.frame.size)
32 | if let layer = viewMain.layer.presentation() {
33 | layer.render(in: UIGraphicsGetCurrentContext()!)
34 | }
35 | UIGraphicsEndImageContext()
36 | }
37 | }
38 |
39 |
--------------------------------------------------------------------------------
/sample/sample.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/sample/sample.xcodeproj/project.xcworkspace/xcuserdata/satoshi.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/swipe-org/swipe/3529d5a1fa808c0885942e521798895bcf6199b7/sample/sample.xcodeproj/project.xcworkspace/xcuserdata/satoshi.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/sample/sample.xcodeproj/xcshareddata/xcschemes/sample.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
37 |
38 |
39 |
40 |
41 |
42 |
52 |
54 |
60 |
61 |
62 |
63 |
69 |
71 |
77 |
78 |
79 |
80 |
82 |
83 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/sample/sample.xcodeproj/xcshareddata/xcschemes/sampletv.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
37 |
38 |
39 |
40 |
41 |
42 |
52 |
54 |
60 |
61 |
62 |
63 |
69 |
71 |
77 |
78 |
79 |
80 |
82 |
83 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/sample/sample.xcodeproj/xcuserdata/satoshi.xcuserdatad/xcschemes/sample.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
37 |
38 |
39 |
40 |
41 |
42 |
52 |
54 |
60 |
61 |
62 |
63 |
69 |
71 |
77 |
78 |
79 |
80 |
82 |
83 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/sample/sample.xcodeproj/xcuserdata/satoshi.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | bug_repro.xcscheme
8 |
9 | orderHint
10 | 3
11 |
12 | sample.xcscheme
13 |
14 | orderHint
15 | 0
16 |
17 | sample.xcscheme_^#shared#^_
18 |
19 | orderHint
20 | 1
21 |
22 | sampletv.xcscheme
23 |
24 | orderHint
25 | 1
26 |
27 | sampletv.xcscheme_^#shared#^_
28 |
29 | orderHint
30 | 2
31 |
32 |
33 | SuppressBuildableAutocreation
34 |
35 | 7473AC481D6D3E5600DA34B1
36 |
37 | primary
38 |
39 |
40 | 74EA71761BD047F0006808EA
41 |
42 | primary
43 |
44 |
45 | 74EA71E01BD0B0EE006808EA
46 |
47 | primary
48 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/sample/sample/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // sample
4 | //
5 | // Created by satoshi on 10/15/15.
6 | // Copyright © 2015 Satoshi Nakajima. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 | var window: UIWindow?
15 |
16 | internal func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
17 | // Override point for customization after application launch.
18 | return true
19 | }
20 |
21 | func applicationWillResignActive(_ application: UIApplication) {
22 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
23 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
24 | }
25 |
26 | func applicationDidEnterBackground(_ application: UIApplication) {
27 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
28 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
29 | }
30 |
31 | func applicationWillEnterForeground(_ application: UIApplication) {
32 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
33 | }
34 |
35 | func applicationDidBecomeActive(_ application: UIApplication) {
36 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
37 | }
38 |
39 | func applicationWillTerminate(_ application: UIApplication) {
40 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
41 | }
42 |
43 | func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
44 | return .landscapeLeft
45 | }
46 | }
47 |
48 |
--------------------------------------------------------------------------------
/sample/sample/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "29x29",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "40x40",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "40x40",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "60x60",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "60x60",
31 | "scale" : "3x"
32 | }
33 | ],
34 | "info" : {
35 | "version" : 1,
36 | "author" : "xcode"
37 | }
38 | }
--------------------------------------------------------------------------------
/sample/sample/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/sample/sample/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
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 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/sample/sample/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | NSAppTransportSecurity
26 |
27 | NSAllowsArbitraryLoads
28 |
29 |
30 | UILaunchStoryboardName
31 | LaunchScreen
32 | UIMainStoryboardFile
33 | Main
34 | UIRequiredDeviceCapabilities
35 |
36 | armv7
37 |
38 | UIRequiresFullScreen
39 |
40 | UISupportedInterfaceOrientations
41 |
42 | UIInterfaceOrientationPortrait
43 | UIInterfaceOrientationLandscapeLeft
44 | UIInterfaceOrientationLandscapeRight
45 |
46 | NSPhotoLibraryUsageDescription
47 | To save generated movie files.
48 |
49 |
50 |
--------------------------------------------------------------------------------
/sample/sample/SwipeBrowser+ex.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwipeBrowser+ex.swift
3 | // sample
4 | //
5 | // Created by satoshi on 8/23/16.
6 | // Copyright © 2016 Satoshi Nakajima. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | extension SwipeBrowser {
12 | @IBAction func export() {
13 | let alert = UIAlertController(title: nil, message: nil, preferredStyle: UIAlertController.Style.actionSheet)
14 | alert.popoverPresentationController?.sourceView = btnExport
15 | alert.addAction(UIAlertAction(title: "Cancel".localized, style: .cancel, handler:nil))
16 | alert.addAction(UIAlertAction(title: "Movie".localized, style: .default) {
17 | (_:UIAlertAction) -> Void in
18 | self.exportAsMovie()
19 | })
20 | alert.addAction(UIAlertAction(title: "GIF Animation".localized, style: .default) {
21 | (_:UIAlertAction) -> Void in
22 | self.exportAsGifAnimation()
23 | })
24 | self.present(alert, animated: true, completion: nil)
25 | }
26 |
27 | func exportAsGifAnimation() {
28 | guard let swipeVC = controller as? SwipeViewController else {
29 | return
30 | }
31 | let docURL = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
32 | let fileURL = docURL.appendingPathComponent("swipe.gif")
33 |
34 | self.viewLoading?.alpha = 1.0
35 | self.labelLoading?.text = "Exporting as a GIF animation...".localized
36 | let exporter = SwipeExporter(swipeViewController: swipeVC, fps:4)
37 | exporter.exportAsGifAnimation(fileURL, startPage: swipeVC.book.pageIndex, pageCount: 3) { (complete, error) -> Void in
38 | self.progress?.progress = Float(exporter.progress)
39 | if complete {
40 | print("GIF animation export done")
41 | UIView.animate(withDuration: 0.2, animations: { () -> Void in
42 | self.viewLoading?.alpha = 0.0
43 | }, completion: { (_:Bool) -> Void in
44 | let activity = UIActivityViewController(activityItems: [fileURL], applicationActivities: nil)
45 | activity.popoverPresentationController?.sourceView = self.btnExport
46 | self.present(activity, animated: true, completion: nil)
47 | })
48 | } else if let error = error {
49 | self.viewLoading?.alpha = 0.0
50 | print("Error", error)
51 | } else {
52 | print("progress", exporter.progress)
53 | }
54 | }
55 | }
56 |
57 | func exportAsMovie() {
58 | guard let swipeVC = controller as? SwipeViewController else {
59 | return
60 | }
61 | let docURL = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
62 | let fileURL = docURL.appendingPathComponent("swipe.mov")
63 |
64 | self.viewLoading?.alpha = 1.0
65 | self.labelLoading?.text = "Exporting as a movie...".localized
66 | let exporter = SwipeExporter(swipeViewController: swipeVC, fps:30, resolution:720.0)
67 | exporter.exportAsMovie(fileURL, startPage: swipeVC.book.pageIndex, pageCount: nil) { (complete, error) -> Void in
68 | self.progress?.progress = Float(exporter.progress)
69 | if complete {
70 | print("Movie export done", exporter.outputSize)
71 | UIView.animate(withDuration: 0.2, animations: { () -> Void in
72 | self.viewLoading?.alpha = 0.0
73 | }, completion: { (_:Bool) -> Void in
74 | let activity = UIActivityViewController(activityItems: [fileURL], applicationActivities: nil)
75 | activity.popoverPresentationController?.sourceView = self.btnExport
76 | self.present(activity, animated: true, completion: nil)
77 | })
78 | } else if let error = error {
79 | self.viewLoading?.alpha = 0.0
80 | print("Error", error)
81 | } else {
82 | print("progress", exporter.progress)
83 | }
84 | }
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/sample/sample/SwipeVideoController.xib:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/sample/sample/iTunesArtwork@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/swipe-org/swipe/3529d5a1fa808c0885942e521798895bcf6199b7/sample/sample/iTunesArtwork@2x.png
--------------------------------------------------------------------------------
/sample/sample/more.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/swipe-org/swipe/3529d5a1fa808c0885942e521798895bcf6199b7/sample/sample/more.png
--------------------------------------------------------------------------------
/sample/sample/movie.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/swipe-org/swipe/3529d5a1fa808c0885942e521798895bcf6199b7/sample/sample/movie.png
--------------------------------------------------------------------------------
/sample/sample/nasa.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/swipe-org/swipe/3529d5a1fa808c0885942e521798895bcf6199b7/sample/sample/nasa.png
--------------------------------------------------------------------------------
/sample/sample/radio.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/swipe-org/swipe/3529d5a1fa808c0885942e521798895bcf6199b7/sample/sample/radio.png
--------------------------------------------------------------------------------
/sample/sample/speech.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/swipe-org/swipe/3529d5a1fa808c0885942e521798895bcf6199b7/sample/sample/speech.png
--------------------------------------------------------------------------------
/sample/sample/text.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/swipe-org/swipe/3529d5a1fa808c0885942e521798895bcf6199b7/sample/sample/text.png
--------------------------------------------------------------------------------
/sample/sampletv/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // sampletv
4 | //
5 | // Created by satoshi on 10/15/15.
6 | // Copyright © 2015 Satoshi Nakajima. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 | var window: UIWindow?
15 |
16 |
17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
18 | // Override point for customization after application launch.
19 | SwipeAssetManager.sharedInstance().flush()
20 | return true
21 | }
22 |
23 | func applicationWillResignActive(_ application: UIApplication) {
24 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
25 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
26 | }
27 |
28 | func applicationDidEnterBackground(_ application: UIApplication) {
29 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
31 | }
32 |
33 | func applicationWillEnterForeground(_ application: UIApplication) {
34 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
35 | }
36 |
37 | func applicationDidBecomeActive(_ application: UIApplication) {
38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
39 | }
40 |
41 | func applicationWillTerminate(_ application: UIApplication) {
42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
43 | }
44 |
45 |
46 | }
47 |
48 |
--------------------------------------------------------------------------------
/sample/sampletv/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Content.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "tv",
5 | "scale" : "1x"
6 | }
7 | ],
8 | "info" : {
9 | "version" : 1,
10 | "author" : "xcode"
11 | }
12 | }
--------------------------------------------------------------------------------
/sample/sampletv/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/sample/sampletv/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "layers" : [
3 | {
4 | "filename" : "Front.imagestacklayer"
5 | },
6 | {
7 | "filename" : "Middle.imagestacklayer"
8 | },
9 | {
10 | "filename" : "Back.imagestacklayer"
11 | }
12 | ],
13 | "info" : {
14 | "version" : 1,
15 | "author" : "xcode"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/sample/sampletv/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Content.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "tv",
5 | "scale" : "1x"
6 | }
7 | ],
8 | "info" : {
9 | "version" : 1,
10 | "author" : "xcode"
11 | }
12 | }
--------------------------------------------------------------------------------
/sample/sampletv/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/sample/sampletv/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "tv",
5 | "scale" : "1x"
6 | }
7 | ],
8 | "info" : {
9 | "version" : 1,
10 | "author" : "xcode"
11 | }
12 | }
--------------------------------------------------------------------------------
/sample/sampletv/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/sample/sampletv/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Content.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "tv",
5 | "scale" : "1x"
6 | }
7 | ],
8 | "info" : {
9 | "version" : 1,
10 | "author" : "xcode"
11 | }
12 | }
--------------------------------------------------------------------------------
/sample/sampletv/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/sample/sampletv/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "layers" : [
3 | {
4 | "filename" : "Front.imagestacklayer"
5 | },
6 | {
7 | "filename" : "Middle.imagestacklayer"
8 | },
9 | {
10 | "filename" : "Back.imagestacklayer"
11 | }
12 | ],
13 | "info" : {
14 | "version" : 1,
15 | "author" : "xcode"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/sample/sampletv/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Content.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "tv",
5 | "scale" : "1x"
6 | }
7 | ],
8 | "info" : {
9 | "version" : 1,
10 | "author" : "xcode"
11 | }
12 | }
--------------------------------------------------------------------------------
/sample/sampletv/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/sample/sampletv/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "tv",
5 | "scale" : "1x"
6 | }
7 | ],
8 | "info" : {
9 | "version" : 1,
10 | "author" : "xcode"
11 | }
12 | }
--------------------------------------------------------------------------------
/sample/sampletv/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/sample/sampletv/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "assets" : [
3 | {
4 | "size" : "1280x768",
5 | "idiom" : "tv",
6 | "filename" : "App Icon - Large.imagestack",
7 | "role" : "primary-app-icon"
8 | },
9 | {
10 | "size" : "400x240",
11 | "idiom" : "tv",
12 | "filename" : "App Icon - Small.imagestack",
13 | "role" : "primary-app-icon"
14 | },
15 | {
16 | "size" : "1920x720",
17 | "idiom" : "tv",
18 | "filename" : "Top Shelf Image.imageset",
19 | "role" : "top-shelf-image"
20 | }
21 | ],
22 | "info" : {
23 | "version" : 1,
24 | "author" : "xcode"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/sample/sampletv/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "tv",
5 | "scale" : "1x"
6 | }
7 | ],
8 | "info" : {
9 | "version" : 1,
10 | "author" : "xcode"
11 | }
12 | }
--------------------------------------------------------------------------------
/sample/sampletv/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/sample/sampletv/Assets.xcassets/LaunchImage.launchimage/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "orientation" : "landscape",
5 | "idiom" : "tv",
6 | "extent" : "full-screen",
7 | "minimum-system-version" : "9.0",
8 | "scale" : "1x"
9 | }
10 | ],
11 | "info" : {
12 | "version" : 1,
13 | "author" : "xcode"
14 | }
15 | }
--------------------------------------------------------------------------------
/sample/sampletv/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/sample/sampletv/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UIMainStoryboardFile
26 | Main
27 | UIRequiredDeviceCapabilities
28 |
29 | arm64
30 |
31 | NSAppTransportSecurity
32 |
33 | NSAllowsArbitraryLoads
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/sample/script/actions.swipe:
--------------------------------------------------------------------------------
1 | {
2 | "pages": [
3 | {
4 | "play":"never",
5 | "elements": [
6 | {
7 | "text": "tap me to start timer", "fontSize":20, "pos": ["50%", "33%"], "w":"90%", "h":"10%", "bc":"#fdd", "focusable":true,
8 | "events": {
9 | "tapped": {
10 | "actions": [
11 | {
12 | "update": {
13 | "text":"tapped",
14 | "bc":"#fee",
15 | "duration":0.5,
16 | "events":{
17 | "completion":{
18 | "actions":[
19 | { "update":{ "text":"tap me", "bc":"#fdd","enabled":false } }
20 | ]
21 | }
22 | }
23 | }
24 | },
25 | {
26 | "timer": {
27 | "duration":1, "repeats":true,
28 | "events":{
29 | "tick":{
30 | "actions":[
31 | { "update":{ "text":"tick", "duration":0.5, "events":{ "completion":{ "actions":[ { "update":{ "text":"tock" }}]}}}}
32 | ]
33 | }
34 | }
35 | }
36 | }
37 | ]
38 | }
39 | }
40 | }
41 | ]
42 | }
43 | ]
44 | }
45 |
--------------------------------------------------------------------------------
/sample/script/bgm/HiddenAgenda.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/swipe-org/swipe/3529d5a1fa808c0885942e521798895bcf6199b7/sample/script/bgm/HiddenAgenda.mp3
--------------------------------------------------------------------------------
/sample/script/bgm/MonkeysSpinningMonkeys.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/swipe-org/swipe/3529d5a1fa808c0885942e521798895bcf6199b7/sample/script/bgm/MonkeysSpinningMonkeys.mp3
--------------------------------------------------------------------------------
/sample/script/bgm/TheDescent.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/swipe-org/swipe/3529d5a1fa808c0885942e521798895bcf6199b7/sample/script/bgm/TheDescent.mp3
--------------------------------------------------------------------------------
/sample/script/bgm/music.swipe:
--------------------------------------------------------------------------------
1 | {
2 | "type":"net.swipe.swipe",
3 | "title": "Scenes with BGM",
4 | "resources":["bgm"],
5 | "templates": {
6 | "elements":{
7 | "caption": { "y":"bottom", "h":"10%", "fontSize":"5%" },
8 | },
9 | "pages":{
10 | "sun": { "bc":"#ff8", "bgm":"TheDescent.mp3", "elements":[ { "template":"caption", "text":"Scene 1"} ] },
11 | "ocean": { "bc":"#77f", "bgm":"HiddenAgenda.mp3", "elements":[ { "template":"caption", "text":"Scene 2"} ] },
12 | "orange": { "bc":"#f80", "bgm":"MonkeysSpinningMonkeys.mp3", "elements":[ { "template":"caption", "text":"Scene 3"} ] },
13 | },
14 | },
15 | "pages":[
16 | {
17 | "template":"sun",
18 | "elements":[
19 | ]
20 | },
21 | {
22 | "template":"sun",
23 | "elements":[
24 | ]
25 | },
26 | {
27 | "template":"sun",
28 | "elements":[
29 | ]
30 | },
31 | {
32 | "template":"ocean",
33 | "elements":[
34 | ]
35 | },
36 | {
37 | "template":"ocean",
38 | "elements":[
39 | ]
40 | },
41 | {
42 | "template":"ocean",
43 | "elements":[
44 | ]
45 | },
46 | {
47 | "template":"orange",
48 | "elements":[
49 | ]
50 | },
51 | {
52 | "template":"orange",
53 | "elements":[
54 | ]
55 | },
56 | {
57 | "template":"orange",
58 | "elements":[
59 | ]
60 | },
61 | ]
62 | }
63 |
--------------------------------------------------------------------------------
/sample/script/details/details_index.swipe:
--------------------------------------------------------------------------------
1 | {
2 | "type":"net.swipe.list",
3 | "rowHeight":"10%",
4 | "languages":[
5 | {"id": "en", "title": "English"},
6 | {"id": "de", "title": "German"}
7 | ],
8 | "strings": {
9 | "anim": {"en":"Animation", "de": "Animation"},
10 | "trans": {"en":"Transition Animation", "de": "Übergangsanimation"},
11 | "loop": {"en":"Loop Animation", "de": "Schleifenanimation"}
12 | },
13 | "sections":[
14 | {
15 | "title":{ "ref":"anim" },
16 | "items":[
17 | { "url":"transition_animation.swipe", "title":{ "ref":"trans" }, "icon":"more.png" },
18 | { "url":"loop_animation.swipe", "title":{ "ref":"loop" }, "icon":"more.png" }
19 | ]
20 | },
21 | {
22 | "title":{ "en":"Language", "de":"Sprache" },
23 | "items":[
24 | {
25 | "url":"multilingual_strings.swipe",
26 | "title":{ "en":"Multilingual Strings", "de":"Mehrsprachige Strings" },
27 | "text":{ "en":"This example has translation", "de":"Dieses beispiel hat übersetzung" },
28 | "icon":"more.png"
29 | }
30 | ]
31 | }
32 | ]
33 | }
34 |
--------------------------------------------------------------------------------
/sample/script/details/loop_animation.swipe:
--------------------------------------------------------------------------------
1 | {
2 | "elements": {
3 | "ball": { "text":"😄", "fontSize":44 },
4 | },
5 | "pages": [
6 | {
7 | "elements": [
8 | { "text":"Loop styles" }
9 | ]
10 | },
11 | {
12 | "elements": [
13 | { "text":"vibrate", "textAlign":"left", "y":-150 },
14 | { "element":"ball", "y":-100, "loop":{ "style":"vibrate" } },
15 | { "text":"blink", "textAlign":"left", "y":-50 },
16 | { "element":"ball", "y":0, "loop":{ "style":"blink" } },
17 | { "text":"wiggle", "textAlign":"left", "y":50 },
18 | { "element":"ball", "y":100, "loop":{ "style":"wiggle" } },
19 | { "text":"spin", "textAlign":"left", "y":150 },
20 | { "element":"ball", "y":200, "loop":{ "style":"spin" } },
21 | ]
22 | },
23 | {
24 | "elements": [
25 | { "text":"Loop count" }
26 | ]
27 | },
28 | {
29 | "elements": [
30 | { "text":"\"count\":1 (default)", "textAlign":"left", "y":-150 },
31 | { "element":"ball", "y":-100, "loop":{ "style":"vibrate" } },
32 | { "text":"\"count\":2", "textAlign":"left", "y":-50 },
33 | { "element":"ball", "y":0, "loop":{ "style":"vibrate", "count":2 } },
34 | { "text":"\"count\":4", "textAlign":"left", "y":50 },
35 | { "element":"ball", "y":100, "loop":{ "style":"vibrate", "count":4 } },
36 | ]
37 | },
38 | {
39 | "elements": [
40 | { "text":"Vibration delta" }
41 | ]
42 | },
43 | {
44 | "elements": [
45 | { "text":"\"delta\":10 (default)", "textAlign":"left", "y":-150 },
46 | { "element":"ball", "y":-100, "loop":{ "style":"vibrate" } },
47 | { "text":"\"delta\":20", "textAlign":"left", "y":-50 },
48 | { "element":"ball", "y":0, "loop":{ "style":"vibrate", "delta":20 } },
49 | { "text":"\"delta\":40", "textAlign":"left", "y":50 },
50 | { "element":"ball", "y":100, "loop":{ "style":"vibrate", "delta":40 } },
51 | ]
52 | },
53 | {
54 | "elements": [
55 | { "text":"Wiggle delta" }
56 | ]
57 | },
58 | {
59 | "elements": [
60 | { "text":"\"delta\":15 (default)", "textAlign":"left", "y":-150 },
61 | { "element":"ball", "y":-100, "loop":{ "style":"wiggle" } },
62 | { "text":"\"delta\":30", "textAlign":"left", "y":-50 },
63 | { "element":"ball", "y":0, "loop":{ "style":"wiggle", "delta":30 } },
64 | { "text":"\"delta\":60", "textAlign":"left", "y":50 },
65 | { "element":"ball", "y":100, "loop":{ "style":"wiggle", "delta":60 } },
66 | ]
67 | },
68 | {
69 | "elements": [
70 | { "text":"Spin direction" }
71 | ]
72 | },
73 | {
74 | "elements": [
75 | { "text":"\"clockwise\":true (default)", "textAlign":"left", "y":-50 },
76 | { "element":"ball", "y":0, "loop":{ "style":"spin" } },
77 | { "text":"\"clockwise\":false", "textAlign":"left", "y":50 },
78 | { "element":"ball", "y":100, "loop":{ "style":"spin", "clockwise":false } },
79 | ]
80 | },
81 | ]
82 | }
83 |
--------------------------------------------------------------------------------
/sample/script/details/multilingual_strings.swipe:
--------------------------------------------------------------------------------
1 | {
2 | "languages":[
3 | {"id": "en", "title": "English"},
4 | {"id": "de", "title": "German"},
5 | ],
6 | "pages":[
7 | {
8 | "strings": {
9 | "good day": {"*":"good day", "de": "Guten Tag"},
10 | "good evening": {"*":"good evening", "de": "guten Abend"},
11 | },
12 |
13 | "elements":[
14 | { "text":{"ref":"good day"}, "h":"20%", "pos":["50%", "12%"]},
15 | { "text":{"ref":"good evening"}, "h":"20%", "pos":["50%", "34%"]},
16 | ],
17 | },
18 | {
19 | "elements":[
20 | { "text":{"*":"good morning", "de": "guten Morgen"}, "h":"20%", "pos":["50%", "12%"]},
21 | { "text":{"*":"good afternoon", "de": "guten Nachmittag"}, "h":"20%", "pos":["50%", "34%"]},
22 | ]
23 | }
24 | ]
25 | }
26 |
--------------------------------------------------------------------------------
/sample/script/details/transition_animation.swipe:
--------------------------------------------------------------------------------
1 | {
2 | "elements": {
3 | "ball": { "text":"⚽️", "textAlign":"left", "x":50 },
4 | },
5 | "pages": [
6 | {
7 | "elements": [
8 | { "text":"\"animation\": \"auto\" (default)" }
9 | ]
10 | },
11 | {
12 | "duration": 1,
13 | "elements": [
14 | { "text":"\"timing\": [0.0, 0.5]", "textAlign":"left", "y":-150 },
15 | { "element":"ball", "y":-100, "to":{ "translate":[200, 0], "timing":[0.0, 0.5] } },
16 | { "text":"\"timing\": [0.25, 0.75]", "textAlign":"left", "y":-50 },
17 | { "element":"ball", "y":0, "to":{ "translate":[200, 0], "timing":[0.25, 0.75] } },
18 | { "text":"\"timing\": [0.5, 1.0]", "textAlign":"left", "y":50 },
19 | { "element":"ball", "y":100, "to":{ "translate":[200, 0], "timing":[0.5, 1.0] } },
20 | { "text":"\"timing\": [0.0, 1.0] (default)", "textAlign":"left", "y":150 },
21 | { "element":"ball", "y":200, "to":{ "translate":[200, 0] } },
22 | ]
23 | },
24 | {
25 | "elements": [
26 | { "text":"\"animation\": \"scroll\"" }
27 | ]
28 | },
29 | {
30 | "animation":"scroll",
31 | "elements": [
32 | { "text":"\"timing\": [0.0, 0.5]", "textAlign":"left", "y":-150 },
33 | { "element":"ball", "y":-100, "to":{ "translate":[200, 0], "timing":[0.0, 0.5] } },
34 | { "text":"\"timing\": [0.25, 0.75]", "textAlign":"left", "y":-50 },
35 | { "element":"ball", "y":0, "to":{ "translate":[200, 0], "timing":[0.25, 0.75] } },
36 | { "text":"\"timing\": [0.5, 1.0]", "textAlign":"left", "y":50 },
37 | { "element":"ball", "y":100, "to":{ "translate":[200, 0], "timing":[0.5, 1.0] } },
38 | { "text":"\"timing\": [0.0, 1.0] (default)", "textAlign":"left", "y":150 },
39 | { "element":"ball", "y":200, "to":{ "translate":[200, 0] } },
40 | ]
41 | }
42 | ]
43 | }
44 |
--------------------------------------------------------------------------------
/sample/script/espresso.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/swipe-org/swipe/3529d5a1fa808c0885942e521798895bcf6199b7/sample/script/espresso.png
--------------------------------------------------------------------------------
/sample/script/espresso/IMG_8152.JPG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/swipe-org/swipe/3529d5a1fa808c0885942e521798895bcf6199b7/sample/script/espresso/IMG_8152.JPG
--------------------------------------------------------------------------------
/sample/script/espresso/IMG_8153.m4v:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/swipe-org/swipe/3529d5a1fa808c0885942e521798895bcf6199b7/sample/script/espresso/IMG_8153.m4v
--------------------------------------------------------------------------------
/sample/script/espresso/IMG_8154.m4v:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/swipe-org/swipe/3529d5a1fa808c0885942e521798895bcf6199b7/sample/script/espresso/IMG_8154.m4v
--------------------------------------------------------------------------------
/sample/script/espresso/IMG_8155.JPG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/swipe-org/swipe/3529d5a1fa808c0885942e521798895bcf6199b7/sample/script/espresso/IMG_8155.JPG
--------------------------------------------------------------------------------
/sample/script/espresso/IMG_8156.JPG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/swipe-org/swipe/3529d5a1fa808c0885942e521798895bcf6199b7/sample/script/espresso/IMG_8156.JPG
--------------------------------------------------------------------------------
/sample/script/espresso/IMG_8159.m4v:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/swipe-org/swipe/3529d5a1fa808c0885942e521798895bcf6199b7/sample/script/espresso/IMG_8159.m4v
--------------------------------------------------------------------------------
/sample/script/espresso/IMG_8160.m4v:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/swipe-org/swipe/3529d5a1fa808c0885942e521798895bcf6199b7/sample/script/espresso/IMG_8160.m4v
--------------------------------------------------------------------------------
/sample/script/espresso/IMG_8161.JPG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/swipe-org/swipe/3529d5a1fa808c0885942e521798895bcf6199b7/sample/script/espresso/IMG_8161.JPG
--------------------------------------------------------------------------------
/sample/script/espresso/IMG_8162.JPG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/swipe-org/swipe/3529d5a1fa808c0885942e521798895bcf6199b7/sample/script/espresso/IMG_8162.JPG
--------------------------------------------------------------------------------
/sample/script/espresso/IMG_8163.m4v:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/swipe-org/swipe/3529d5a1fa808c0885942e521798895bcf6199b7/sample/script/espresso/IMG_8163.m4v
--------------------------------------------------------------------------------
/sample/script/espresso/IMG_8166.m4v:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/swipe-org/swipe/3529d5a1fa808c0885942e521798895bcf6199b7/sample/script/espresso/IMG_8166.m4v
--------------------------------------------------------------------------------
/sample/script/espresso/IMG_8167.m4v:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/swipe-org/swipe/3529d5a1fa808c0885942e521798895bcf6199b7/sample/script/espresso/IMG_8167.m4v
--------------------------------------------------------------------------------
/sample/script/espresso/IMG_8168.JPG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/swipe-org/swipe/3529d5a1fa808c0885942e521798895bcf6199b7/sample/script/espresso/IMG_8168.JPG
--------------------------------------------------------------------------------
/sample/script/espresso/IMG_8169.m4v:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/swipe-org/swipe/3529d5a1fa808c0885942e521798895bcf6199b7/sample/script/espresso/IMG_8169.m4v
--------------------------------------------------------------------------------
/sample/script/espresso/IMG_8171.m4v:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/swipe-org/swipe/3529d5a1fa808c0885942e521798895bcf6199b7/sample/script/espresso/IMG_8171.m4v
--------------------------------------------------------------------------------
/sample/script/espresso/espresso_machine_l.swipe:
--------------------------------------------------------------------------------
1 | {
2 | "type":"net.swipe.swipe",
3 | "title": "Breville BES870XL Quick Start Guide",
4 | "dimension":[640.0, 0.0],
5 | "orientation":"landscape",
6 | "resources":["es1"],
7 | "templates": {
8 | "pages":{
9 | "*":{
10 | "bc":"#333",
11 | },
12 | },
13 | "elements":{
14 | "full": { "w":"fill", "h":"fill" },
15 | "caption" : { "y":"bottom", "h":100, "x":"5%", "w":"90%" },
16 | "play" : { "x":"center", "y":"center", "w":80, "h":80, "text":"▶︎", "fontSize":40.0, "textColor":"#fffe",
17 | "borderWidth":3.0, "borderColor":"#fffe",
18 | "bc":"#000a", "cornerRadius":40, "action":"play" },
19 | },
20 | },
21 | "markdown":{
22 | "shadow":{ "color":"black", "offset":[2,2], "blurRadius":2 },
23 | "styles":{
24 | "#":{ "color":"white", "alignment":"center", "font":{ "size":24 } },
25 | "##":{ "color":"white", "font":{ "size":20 } },
26 | },
27 | },
28 | "pages":[
29 | {
30 | "elements":[
31 | { "template":"full", "img":"IMG_8152.JPG" },
32 | { "template":"caption", "markdown":["# Breville BES870XL","# Quick Start Guide"] },
33 | ]
34 | },
35 | {
36 | "elements":[
37 | { "template":"full", "video":"IMG_8153.m4v" },
38 | { "template":"caption", "markdown":"## 1. Attach the filter basket to the portafilter." },
39 | { "template":"play" },
40 | ]
41 | },
42 | {
43 | "elements":[
44 | { "template":"full", "video":"IMG_8154.m4v" },
45 | { "template":"caption", "markdown":"## 2. Set the filter below the grinder, and tap the handle once." },
46 | { "template":"play" },
47 | ]
48 | },
49 | {
50 | "elements":[
51 | { "template":"full", "img":"IMG_8155.JPG" },
52 | { "template":"caption", "markdown":"## 3. It is normal for the portafilter to appear overfilled." }
53 | ]
54 | },
55 | {
56 | "elements":[
57 | { "template":"full", "img":"IMG_8156.JPG" },
58 | { "template":"caption", "markdown":"## 4. Tamp the gound coffee down firmly using 30-40lbs of pressure." }
59 | ]
60 | },
61 | {
62 | "elements":[
63 | { "template":"full", "video":"IMG_8159.m4v" },
64 | { "template":"caption", "markdown":"## 5. Insert the Portafilter underneath the group head, and rotate the handle towards the center." },
65 | { "template":"play" },
66 | ]
67 | },
68 | {
69 | "elements":[
70 | { "template":"full", "video":"IMG_8160.m4v" },
71 | { "template":"caption", "markdown":"## 6. Press the 1 CPU button once to extract a single shot of expresso at the preset volume." },
72 | { "template":"play" },
73 | ]
74 | },
75 | {
76 | "elements":[
77 | { "template":"full", "img":"IMG_8161.JPG" },
78 | { "template":"caption", "markdown":"## 7. The espresso will start flow after 4-7 seconds and should be the consistency of driping honey." }
79 | ]
80 | },
81 | {
82 | "elements":[
83 | { "template":"full", "img":"IMG_8162.JPG" },
84 | { "template":"caption", "markdown":"## 8. Fill the jug with milk just below the \"V\" at the bottom of the spout." },
85 | ]
86 | },
87 | {
88 | "elements":[
89 | { "template":"full", "video":"IMG_8163.m4v" },
90 | { "template":"caption", "markdown":"## 9. Set the STEAM/HOT WATER dial to the STEAM position." },
91 | { "template":"play" },
92 | ]
93 | },
94 | {
95 | "elements":[
96 | { "template":"full", "video":"IMG_8171.m4v" },
97 | { "template":"caption", "markdown":"## 10. With the milk spinning, slowly lower the jug to introduce air into the milk." },
98 | { "template":"play" },
99 | ]
100 | },
101 | {
102 | "elements":[
103 | { "template":"full", "video":"IMG_8166.m4v" },
104 | { "template":"caption", "markdown":"## 11. Tap the jug on the table to collapse large bubbles." },
105 | { "template":"play" },
106 | ]
107 | },
108 | {
109 | "elements":[
110 | { "template":"full", "video":"IMG_8167.m4v" },
111 | { "template":"caption", "markdown":"## 12. Pour milk directly into the expresso." },
112 | { "template":"play" },
113 | ]
114 | },
115 | {
116 | "duration":5.0,
117 | "elements":[
118 | { "template":"full", "img":"IMG_8168.JPG", "to":{ "scale":1.2 } },
119 | { "template":"caption", "markdown":"# Enjoy your late!" }
120 | ]
121 | },
122 | ]
123 | }
124 |
--------------------------------------------------------------------------------
/sample/script/index.swipe:
--------------------------------------------------------------------------------
1 | {
2 | "type":"net.swipe.list",
3 | "rowHeight":"10%",
4 | "items":[
5 | { "url":"swipe.swipe", "title":"Swipe Overview", "icon":"Icon-180.png" },
6 | { "url":"tutorial.swipe", "title":"Swipe Tutorial", "icon":"Icon-180.png" },
7 | { "url":"empty.swipe", "title":"Empty", "icon":"Icon-180.png" },
8 | { "url":"dice.swipe", "title":"Dice (scale, rotate, translate)", "icon":"https://satoshi.blogs.com/swipe/Dice1.png" },
9 | { "url":"dice2.swipe", "title":"Dice (loop, scale)", "icon":"https://satoshi.blogs.com/swipe/Dice1.png" },
10 | { "url":"videostream.swipe", "title":"Streaming Video", "icon":"movie.png" },
11 | { "url":"radiostream.swipe", "title":"Internet Radio Stations", "icon":"radio.png" },
12 | { "url":"markdown.swipe", "title":"Rich Text", "icon":"text.png" },
13 | { "url":"speech.swipe", "title":"Speech", "icon":"speech.png" },
14 | { "url":"vectors.swipe", "title":"Vectors", "icon":"Icon-180.png" },
15 | { "url":"nasa.swipe", "title":"NASA HQ Images", "icon":"nasa.png" },
16 | { "url":"espresso_machine_l.swipe", "title":"Espresso Machine", "icon":"espresso.png" },
17 | { "url":"actions.swipe", "title":"Events & Actions", "icon":"Icon-180.png" },
18 | { "url":"update.swipe", "title":"Properties", "icon":"Icon-180.png" },
19 | { "url":"list.swipe", "title":"List", "icon":"Icon-180.png" },
20 | { "url":"network.swipe", "title":"Network", "icon":"Icon-180.png" },
21 | { "url":"details_index.swipe", "title":"Property Details", "icon":"more.png" },
22 | { "url":"http://www.swipe.net/index.swipe", "title":"More...", "text":"Load a list from a remote host", "icon":"more.png" },
23 | { "url":"http://satoshi.blogs.com/swipe/index2.swipe", "title":"More 2...", "text":"Load a list from a remote host", "icon":"more.png" },
24 | { "url":"tv_index.swipe", "title":"TV", "icon":"more.png" },
25 | { "url":"https://raw.githubusercontent.com/swipe-org/swipe-sample/master/index.swipe", "title":"Test Cases", "icon":"Icon-180.png" },
26 | { "url":"https://raw.githubusercontent.com/swipe-org/swipe-sample/staging/index.swipe", "title":"Test Cases (Staging)", "icon":"Icon-180.png" }
27 | ]
28 | }
29 |
--------------------------------------------------------------------------------
/sample/script/list.swipe:
--------------------------------------------------------------------------------
1 | {
2 | "templates": {
3 | "elements": {
4 | "even": {
5 | "bc":"#eef",
6 | "elements": [
7 | { "img":"icon-180.png", "pos":[20,"50%"], "w":20 },
8 | {
9 | "x":40, "w":"80%", "textAlign":"left", "fontSize":20,
10 | "text":{"valueOf":{"id":"aList", "property":{"items":{"data":{"person":{"id":"first"}}}}}}
11 | }
12 | ]
13 | },
14 | "odd": {
15 | "bc":"#efe",
16 | "elements": [
17 | { "img":"icon-180.png", "pos":["90%","50%"], "w":20},
18 | {
19 | "x":0, "w":"80%", "textAlign":"right", "fontSize":20,
20 | "text":{"valueOf":{"id":"aList", "property":{"items":{"data":{"person":{"id":"first"}}}}}}
21 | }
22 | ]
23 | }
24 | }
25 | },
26 | "pages": [
27 | {
28 | "id": "main",
29 | "play":"never",
30 | "elements": [
31 | { "bc":"#fdd"},
32 | { "h":"8%", "w":"45%", "pos":["25%","95%"],"text":"selected", "textAlign":"right"},
33 | { "id":"echo", "h":"8%", "w":"45%", "pos":["75%","95%"],"text":""},
34 | { "text":"Tap on List Items", "h":"8%", "pos":["50%", "5%"]},
35 | {
36 | "id": "aList",
37 | "h":"80%",
38 | "w":"90%",
39 | "bc":"#ffe",
40 | "pos":["50%","50%"],
41 | "list": {
42 | "selectedItem":2,
43 | "items": [
44 | { "elements":[ {"template":"even" }], "data":{ "person": { "id": { "first":"fred", "last":"flintstone"}}}},
45 | { "elements":[ {"template":"odd" }], "data":{ "person": { "id": { "first":"wilma", "last":"flintstone"}}}},
46 | { "elements":[ {"template":"even" }], "data":{ "person": { "id": { "first":"pebbles", "last":"flintstone"}}}},
47 | { "elements":[ {"template":"odd" }], "data":{ "person": { "id": { "first":"barney", "last":"rubble"}}}},
48 | { "elements":[ {"template":"even" }], "data":{ "person": { "id": { "first":"betty", "last":"rubble"}}}},
49 | { "elements":[ {"template":"odd" }], "data":{ "person": { "id": { "first":"bambam", "last":"rubble"}}}}
50 | ]
51 | },
52 | "events": {
53 | "rowSelected": {
54 | "actions": [
55 | { "update": { "id":"echo", "text":{"valueOf":{"id":"aList", "property":"selectedItem"}}}}
56 | ]
57 | }
58 | }
59 | }
60 | ],
61 | "events": {
62 | "load": {
63 | "actions": [
64 | { "update": { "id":"echo", "text":{"valueOf":{"id":"aList", "property":"selectedItem"}}}}
65 | ]
66 | }
67 | }
68 | }
69 | ]
70 | }
71 |
--------------------------------------------------------------------------------
/sample/script/markdown.swipe:
--------------------------------------------------------------------------------
1 | {
2 | "title":"Text",
3 | "markdown":{
4 | "shadow":{ "color":"blue", "offset":[-2,2], "radius":3, "opacity":0.5 },
5 | "styles":{
6 | "#":{ "color":"#008", "font":{ "size":"5%", "name":["foo", "Courier"] }},
7 | "##":{ "color":"#080", "font":{ "size":"4.5%", "name":"Chalkboard SE" }},
8 | "###":{ "color":"#800", "font":{ "size":"4%", "name":"Helvetica" }},
9 | "####":{ "color":"#800", "font":{ "size":"3.5%"}},
10 | "#5":{ "color":"red", "font":{ "size":"3.5%"}},
11 | "*":{ "color":"#880", "font":{ "size":"3%", "name":"Helvetica" }},
12 | "-":{ "color":"#808", "font":{ "size":"3%", "name":"Helvetica" }, "prefix":"◆ "},
13 | "+":{ "color":"#808", "font":{ "size":"3%", "name":"Helvetica" }, "prefix":"✅ "},
14 | "```":{ "color":"#808", "prefix":"| " },
15 | },
16 | },
17 | "templates": {
18 | "elements":{
19 | "slide": { "x":"10%", "y":"10%", "w":"80%", "h":"80%", "bc":"#ffc" },
20 | },
21 | },
22 | "pages":[
23 | {
24 | "elements":[
25 | { "template":"slide","markdown":[
26 | "# スマートフォンの時代に相応しい『漫画・アニメ体験』と、その実現に必要なマークアップ言語の提案",
27 | ]},
28 | ],
29 | },
30 | {
31 | "elements":[
32 | { "template":"slide", "markdown":[
33 | "#5 Keywords",
34 | "- Comic (Manga)",
35 | "- Multi-media eBook",
36 | "- Smartphone User Experience",
37 | "+ Native vs. HTML5",
38 | "+ Domain-Specific Language",
39 | "+ Declarative Language",
40 | ]},
41 | ],
42 | },
43 | {
44 | "elements":[
45 | { "template":"slide", "markdown":[
46 | "# Hello World",
47 | "## Hello World",
48 | "### Hello World",
49 | "#### Hello World",
50 | "Hello World",
51 | "Hello World",
52 | "- Hello World",
53 | "- Hello World",
54 | "- Hello World",
55 | ]},
56 | ],
57 | },
58 | {
59 | "elements":[
60 | { "template":"slide", "markdown":[
61 | "Hello World",
62 | "Hello World",
63 | "```",
64 | "Hello World {",
65 | " Hello World {",
66 | " Hello World",
67 | " }",
68 | "} Hello World",
69 | "```",
70 | "Hello World",
71 | "Hello World",
72 | ]},
73 | ],
74 | },
75 | {
76 | "elements":[
77 | { "template":"slide", "text":"Hello World" },
78 | ],
79 | },
80 | ],
81 | }
82 |
--------------------------------------------------------------------------------
/sample/script/nasa/nasa.swipe:
--------------------------------------------------------------------------------
1 | {
2 | "type":"net.swipe.swipe",
3 | "title": "NASA HQ Images",
4 | "orientation":"landscape",
5 | "viewstate":false,
6 | "resources":["nasa", "bgm"],
7 | "templates": {
8 | "pages":{
9 | "descent": { "duration":5.0, "bgm":"TheDescent.mp3" },
10 | "agenda": { "duration":5.0, "bgm":"HiddenAgenda.mp3" },
11 | "monkey": { "duration":5.0, "bgm":"MonkeysSpinningMonkeys.mp3" },
12 | },
13 | "elements":{
14 | "caption": { "y":"top", "h":"5%" },
15 | "full": { "x":"center", "y":"bottom", "w":"fill", "to":{ "scale":1.1 } },
16 | },
17 | },
18 | "markdown":{
19 | "shadow":{ "color":"#000", "offset":[-2,2] },
20 | "styles":{
21 | "+":{ "color":"#fff8", "prefix":"♫ ", "alignment":"right",
22 | "font":{ "size":"3.5%", "name":"Helvetica" }},
23 | },
24 | },
25 | "pages":[
26 | {
27 | "template":"descent",
28 | "elements":[
29 | { "template":"full", "img":"nasa001.jpg" },
30 | { "template":"caption", "markdown":"+ The Decent by Kevin MacLeod"},
31 | ]
32 | },
33 | {
34 | "template":"descent",
35 | "elements":[
36 | { "template":"full", "img":"nasa002.jpg"},
37 | ]
38 | },
39 | {
40 | "template":"descent",
41 | "elements":[
42 | { "template":"full", "img":"nasa003.jpg" },
43 | ]
44 | },
45 | {
46 | "template":"descent",
47 | "elements":[
48 | { "template":"full", "img":"nasa004.jpg" },
49 | ]
50 | },
51 | {
52 | "template":"agenda",
53 | "elements":[
54 | { "template":"full", "img":"nasa005.jpg" },
55 | { "template":"caption", "markdown":"+ Hidden Agenda by Kevin MacLeod"},
56 | ]
57 | },
58 | {
59 | "template":"agenda",
60 | "elements":[
61 | { "template":"full", "img":"nasa006.jpg" },
62 | ]
63 | },
64 | {
65 | "template":"agenda",
66 | "elements":[
67 | { "template":"full", "img":"nasa007.jpg" },
68 | ]
69 | },
70 | {
71 | "template":"agenda",
72 | "elements":[
73 | { "template":"full", "img":"nasa008.jpg" },
74 | ]
75 | },
76 | {
77 | "template":"agenda",
78 | "elements":[
79 | { "template":"full", "img":"nasa009.jpg" },
80 | ]
81 | },
82 | {
83 | "template":"monkey",
84 | "elements":[
85 | { "template":"full", "img":"nasa010.jpg" },
86 | { "template":"caption", "markdown":"+ Monkeys Spinning Monkeys by Kevin MacLeod"},
87 | ]
88 | },
89 | {
90 | "template":"monkey",
91 | "elements":[
92 | { "template":"full", "img":"nasa011.jpg" },
93 | ]
94 | },
95 | {
96 | "template":"monkey",
97 | "elements":[
98 | { "template":"full", "img":"nasa012.jpg" },
99 | ]
100 | },
101 | {
102 | "template":"monkey",
103 | "elements":[
104 | { "template":"full", "img":"nasa013.jpg" },
105 | ]
106 | },
107 | ]
108 | }
109 |
--------------------------------------------------------------------------------
/sample/script/nasa/nasa000.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/swipe-org/swipe/3529d5a1fa808c0885942e521798895bcf6199b7/sample/script/nasa/nasa000.jpg
--------------------------------------------------------------------------------
/sample/script/nasa/nasa001.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/swipe-org/swipe/3529d5a1fa808c0885942e521798895bcf6199b7/sample/script/nasa/nasa001.jpg
--------------------------------------------------------------------------------
/sample/script/nasa/nasa002.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/swipe-org/swipe/3529d5a1fa808c0885942e521798895bcf6199b7/sample/script/nasa/nasa002.jpg
--------------------------------------------------------------------------------
/sample/script/nasa/nasa003.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/swipe-org/swipe/3529d5a1fa808c0885942e521798895bcf6199b7/sample/script/nasa/nasa003.jpg
--------------------------------------------------------------------------------
/sample/script/nasa/nasa004.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/swipe-org/swipe/3529d5a1fa808c0885942e521798895bcf6199b7/sample/script/nasa/nasa004.jpg
--------------------------------------------------------------------------------
/sample/script/nasa/nasa005.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/swipe-org/swipe/3529d5a1fa808c0885942e521798895bcf6199b7/sample/script/nasa/nasa005.jpg
--------------------------------------------------------------------------------
/sample/script/nasa/nasa006.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/swipe-org/swipe/3529d5a1fa808c0885942e521798895bcf6199b7/sample/script/nasa/nasa006.jpg
--------------------------------------------------------------------------------
/sample/script/nasa/nasa007.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/swipe-org/swipe/3529d5a1fa808c0885942e521798895bcf6199b7/sample/script/nasa/nasa007.jpg
--------------------------------------------------------------------------------
/sample/script/nasa/nasa008.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/swipe-org/swipe/3529d5a1fa808c0885942e521798895bcf6199b7/sample/script/nasa/nasa008.jpg
--------------------------------------------------------------------------------
/sample/script/nasa/nasa009.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/swipe-org/swipe/3529d5a1fa808c0885942e521798895bcf6199b7/sample/script/nasa/nasa009.jpg
--------------------------------------------------------------------------------
/sample/script/nasa/nasa010.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/swipe-org/swipe/3529d5a1fa808c0885942e521798895bcf6199b7/sample/script/nasa/nasa010.jpg
--------------------------------------------------------------------------------
/sample/script/nasa/nasa011.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/swipe-org/swipe/3529d5a1fa808c0885942e521798895bcf6199b7/sample/script/nasa/nasa011.jpg
--------------------------------------------------------------------------------
/sample/script/nasa/nasa012.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/swipe-org/swipe/3529d5a1fa808c0885942e521798895bcf6199b7/sample/script/nasa/nasa012.jpg
--------------------------------------------------------------------------------
/sample/script/nasa/nasa013.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/swipe-org/swipe/3529d5a1fa808c0885942e521798895bcf6199b7/sample/script/nasa/nasa013.jpg
--------------------------------------------------------------------------------
/sample/script/network.swipe:
--------------------------------------------------------------------------------
1 | {
2 | "pages": [
3 | {
4 | "play":"never",
5 | "elements": [
6 | {
7 | "text":"HTTP GET", "pos":["50%", 80], "w":"80%", "h":50, "borderWidth":1, "borderColor":"black", "cornerRadius":10, "focusable":true,
8 | "events":{
9 | "tapped":{
10 | "actions":[
11 | { "update":{"id":"label", "text":"Loading ...", "textColor":"black" } },
12 | { "update":{"id":"error", "text":"", "opacity":0 } },
13 | {
14 | "get":{
15 | "source": {"url":"http://www.stoppani.net/swipe/simpledata.txt"},
16 | "events": {
17 | "error": {
18 | "params": {"message":{"type":"string"}},
19 | "actions": [
20 | { "update":{"id":"label", "text":"Error. Try Again", "textColor":"red" } },
21 | { "update":{"id":"error", "text":{"valueOf":{"property":{"params":"message"}}}, "opacity":1, "duration":0.4}}
22 | ]
23 | },
24 | "completion": {
25 | "params": {"caption":{"type":"string"}, "imageURL":{"type":"string"}},
26 | "actions": [
27 | { "update":{"id":"label", "text":{"valueOf":{"property":{"params":"caption"}}}}},
28 | { "update":{"id":"image", "img":{"valueOf":{"property":{"params":"imgURL"}}}}}
29 | ]
30 | }
31 | }
32 | }
33 | }
34 | ]
35 | }
36 | }},
37 | {"id":"label", "h":50, "pos":["50%", 130] },
38 | {"id":"image", "w":150, "h":150, "pos":["50%", 240], "img":"more.png" },
39 | {"id":"error", "textColor":"red", "w":"98%", "h":200, "pos":["50%", 420], "fontSize":18 },
40 | ]
41 | }
42 | ]
43 | }
44 |
--------------------------------------------------------------------------------
/sample/script/radiostream.swipe:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Internet Radios",
3 | "orientation":"landscape",
4 | "templates": {
5 | "pages":{
6 | "*":{
7 | "play":"always",
8 | },
9 | },
10 | },
11 | "pages":[
12 | {
13 | "elements":[
14 | { "radio":"http://play.radiopanteon.pl:8000/stream" },
15 | { "text":"Radio Panteon" },
16 | ],
17 | },
18 | {
19 | "elements":[
20 | { "radio":"http://dir.xiph.org/listen/3754848/listen.m3u" },
21 | { "text":"Russian Hit - PYCCKUU XUT" },
22 | ],
23 | },
24 | {
25 | "elements":[
26 | { "radio":"http://dir.xiph.org/listen/3618988/listen.m3u" },
27 | { "text":"MEGA RADIO" },
28 | ],
29 | },
30 | {
31 | "elements":[
32 | { "radio":"http://dir.xiph.org/listen/4061925/listen.m3u" },
33 | { "text":"DUSHEVNOE RADIO" },
34 | ],
35 | },
36 | {
37 | "elements":[
38 | { "radio":"http://dir.xiph.org/listen/3716076/listen.m3u" },
39 | { "text":"I Love Radio" },
40 | ],
41 | },
42 | {
43 | "elements":[
44 | { "radio":"http://dir.xiph.org/listen/3956748/listen.m3u" },
45 | { "text":"ABCLounge" },
46 | ],
47 | },
48 | {
49 | "elements":[
50 | { "radio":"http://dir.xiph.org/listen/3851921/listen.m3u" },
51 | { "text":"sunshine live" },
52 | ],
53 | },
54 | {
55 | "elements":[
56 | { "radio":"http://dir.xiph.org/listen/3753780/listen.m3u" },
57 | { "text":"Mundo Livre FM Curitiba" },
58 | ],
59 | },
60 | ]
61 | }
62 |
--------------------------------------------------------------------------------
/sample/script/speech.swipe:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Speech",
3 | "voices":{
4 | "*":{ "lang":"en-US" },
5 | "american":{ "lang":"en-US" },
6 | "japanese":{ "lang":"ja-JP" },
7 | "british":{ "lang":"en-GB" },
8 | "french":{ "lang":"fr-FR" },
9 | "german":{ "lang":"de-DE" },
10 | "vader":{ "pitch":2.0, "rate":0.2 },
11 | },
12 | "pages": [
13 | {
14 | "speech":{ "text":"Hello World" },
15 | "elements": [
16 | { "text":"Hello World" }
17 | ]
18 | },
19 | {
20 | "speech":{ "text":"こんにちは", "voice":"japanese" },
21 | "elements": [
22 | { "text":"こんにちは" }
23 | ]
24 | },
25 | {
26 | "speech":{ "text":"Bonjour", "voice":"french" },
27 | "elements": [
28 | { "text":"Bonjour" }
29 | ]
30 | },
31 | {
32 | "speech":{ "text":"Guten Tag", "voice":"german" },
33 | "elements": [
34 | { "text":"Guten Tag" }
35 | ]
36 | },
37 | {
38 | "speech":{ "text":"I'm an American in New York.", "voice":"american" },
39 | "elements": [
40 | { "text":"I'm an American in New York." }
41 | ]
42 | },
43 | {
44 | "speech":{ "text":"I'm an Englishman in New York.", "voice":"british" },
45 | "elements": [
46 | { "text":"I'm an Englishman in New York." }
47 | ]
48 | },
49 | {
50 | "speech":{ "text":"I'm a Japanese in New York", "voice":"japanese" },
51 | "elements": [
52 | { "text":"I'm a Japanese in New York." }
53 | ]
54 | },
55 | {
56 | "speech":{ "text":"May the force be with you.", "voice":"vader" },
57 | "elements": [
58 | { "text":"May the force be with you." }
59 | ]
60 | },
61 | ]
62 | }
--------------------------------------------------------------------------------
/sample/script/tutorial/Icon-180.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/swipe-org/swipe/3529d5a1fa808c0885942e521798895bcf6199b7/sample/script/tutorial/Icon-180.png
--------------------------------------------------------------------------------
/sample/script/tutorial/chara_walk.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/swipe-org/swipe/3529d5a1fa808c0885942e521798895bcf6199b7/sample/script/tutorial/chara_walk.png
--------------------------------------------------------------------------------
/sample/script/tutorial/empty.swipe:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Swipe",
3 | "dimension":[1280, 0],
4 |
5 | "pages":[{
6 | "elements":[{
7 | "x":100, "y":100, "w":100, "h":100,
8 | "bc":"#00f"
9 | }],
10 | }]
11 | }
12 |
--------------------------------------------------------------------------------
/sample/script/tutorial/epsilon.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/swipe-org/swipe/3529d5a1fa808c0885942e521798895bcf6199b7/sample/script/tutorial/epsilon.gif
--------------------------------------------------------------------------------
/sample/script/tutorial/izumi_06.mov:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/swipe-org/swipe/3529d5a1fa808c0885942e521798895bcf6199b7/sample/script/tutorial/izumi_06.mov
--------------------------------------------------------------------------------
/sample/script/tutorial/shuttle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/swipe-org/swipe/3529d5a1fa808c0885942e521798895bcf6199b7/sample/script/tutorial/shuttle.png
--------------------------------------------------------------------------------
/sample/script/tutorial/sound01.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/swipe-org/swipe/3529d5a1fa808c0885942e521798895bcf6199b7/sample/script/tutorial/sound01.wav
--------------------------------------------------------------------------------
/sample/script/tutorial/swipe.swipe:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Swipe",
3 | "dimension":[1280, 0],
4 | "orientation":"landscape",
5 | "templates": {
6 | "elements":{
7 | "body": { "x":"center", "w":"66.7%" },
8 | "code": { "x":"4%", "y":"4%", "w":"44%", "h":"92%", "bc":"#eff" },
9 | "demo": { "x":"52%", "y":"4%", "w":"44%", "h":"92%" },
10 | "logo": {
11 | "elements":[
12 | {
13 | "id":"back", "w":192, "h":192, "x":"center", "y":"center", "bc":"#00ACC1",
14 | },
15 | {
16 | "id":"frame", "w":192, "h":192, "x":"center", "y":"center", "bc":"#00ACC1",
17 | "clip":true,
18 | "elements": [
19 | { "w":192, "h":288, "bc":"#00BCD4",
20 | "anchor":[0, 144], "pos":["50%","50%"], "rotate":-45,
21 | "shadow":{ "offset":[-0.7,0], "opacity":0.1, "radius":1 } },
22 | { "w":288, "h":40, "bc":"#80DEEA",
23 | "anchor":[144, 40], "pos":["50%","50%"], "rotate":-45,
24 | "shadow":{ "offset":[0,1.4], "opacity":0.2, "radius":2 } },
25 | { "w":40, "h":40, "fillColor":"#E0F7FA", "path":"ellipse",
26 | "anchor":[20, 40], "pos":["50%","50%"], "rotate":-45,
27 | "shadow":{ "offset":[-2,2], "opacity":0.3, "radius":3 } },
28 | ],
29 | },
30 | ]
31 | },
32 | },
33 | "pages":{
34 | "*": { "bc":"#ddf" },
35 | "demo": { "bc":"#fff" },
36 | },
37 | },
38 | "markdown":{
39 | "styles": {
40 | "#":{ "color":"#616161", "font":{ "size":92, "name":"Helvetica-Bold" }, "alignment":"center" },
41 | "#2":{ "color":"#616161", "font":{ "size":60, "name":"Helvetica-Bold" }, "alignment":"center" },
42 | "##":{ "color":"#008", "font":{ "size":60 } },
43 | "###":{ "color":"#040", "font":{ "size":48, "name":"HelveticaNeue-Italic" } },
44 | "-":{ "font":{ "size":42 } },
45 | "*":{ "font":{ "size":42 } },
46 | "```": { "font":{ "size":24, "name":"Damascus" } },
47 | }
48 | },
49 |
50 | "pages":[
51 | {
52 | "elements":[
53 | { "template":"body", "markdown":[
54 | "# Swipe",
55 | ]},
56 | { "template":"logo", "x":300, "w":192,
57 | "elements":[
58 | {
59 | "id":"back", "w":128, "h":176, "cornerRadius":12,
60 | "shadow":{ "offset":[0,8], "opacity":0.2, "radius":8 },
61 | },
62 | {
63 | "id":"frame", "w":128, "h":176, "cornerRadius":12,
64 | },
65 | ]
66 | },
67 | ],
68 | },
69 |
70 | {
71 | "elements":[
72 | { "template":"body", "markdown":[
73 | "## Problems",
74 | "- Native vs. HTML5 dilemma",
75 | "- Not enough developers",
76 | "- Too expensive to develop apps",
77 | "- ePub is not designed for multi-media",
78 | "### Hard to innovate without programming!",
79 | ]},
80 | ]
81 | },
82 |
83 | {
84 | "elements":[
85 | { "template":"body", "markdown":[
86 | "## Media Format Evolutions",
87 | "- Book & Magazine for Paper",
88 | "- Movie & Animation for TV",
89 | "- HTML for PC",
90 | "- PowerPoint & Keynote for Projector",
91 | "- ePub for eBook",
92 | "### Let's design a new media format specificaly for touch-enabled smart devices!",
93 | ]},
94 | ]
95 | },
96 |
97 | {
98 | "elements":[
99 | { "template":"body", "markdown":[
100 | "## Target Applications",
101 | "- Interactive Comics",
102 | "- Sound Novels",
103 | "- Graphical Audio Books",
104 | "- Interactive Videos",
105 | "- Media-rich Presentations",
106 | "- Interactive Arts",
107 | ]},
108 | ]
109 | },
110 |
111 | {
112 | "elements":[
113 | { "template":"body", "markdown":[
114 | "## Design Principles",
115 | "- Optimized for touch-enabled devices",
116 | "- 100% declarative (no programming)",
117 | "- Rich & interactive animations",
118 | "- Re-invent the video experience",
119 | "- Customizable page-transitions",
120 | "- Designer friendly",
121 | "- Lighweight & portable",
122 | ]},
123 | ]
124 | },
125 |
126 | {
127 | "elements":[
128 | { "template":"body", "markdown":[
129 | "#2 Swipe Engine & Language",
130 | ]},
131 | ],
132 | },
133 |
134 | {
135 | "elements":[
136 | { "template":"body", "markdown":[
137 | "## Swipe Engine",
138 | "- enables media-rich/animated documents",
139 | "- is optimized for touch-enabled devices",
140 | "- is portable",
141 | "- is lightweight",
142 | "- is open source",
143 | "### github.com/snakajima/swipe",
144 | ]},
145 | ]
146 | },
147 |
148 | {
149 | "elements":[
150 | { "template":"body", "markdown":[
151 | "## Swipe Engine",
152 | "- is not a general purpose platform",
153 | "- is not a programming environment",
154 | "- is not a game engine",
155 | "- does not replace ePub",
156 | ]},
157 | ]
158 | },
159 | {
160 | "elements":[
161 | { "template":"body", "markdown":[
162 | "## Swipe Language",
163 | "- is a domain-specific language",
164 | "- is declarative (like HTML)",
165 | "- is based on JSON (not XML)",
166 | "- is designer-friendly",
167 | ]},
168 | ]
169 | },
170 |
171 | {
172 | "elements":[
173 | { "template":"body", "markdown":[
174 | "## Status",
175 | "### Now (October 2015)",
176 | "- Specification (0.1)",
177 | "- iOS/tvOS Runtime (0.1)",
178 | "### Next Step",
179 | "- Further improvement based on feedback",
180 | "- Android/HTML5/Windows Runtime",
181 | "- Authoring Tool",
182 | "#### https://github.com/snakajima/swipe",
183 | "#### http://www.swipe.net",
184 | ]},
185 | ]
186 | },
187 |
188 | ]
189 | }
190 |
--------------------------------------------------------------------------------
/sample/script/tv/focus.swipe:
--------------------------------------------------------------------------------
1 | {
2 | "templates":{
3 | "elements":{
4 | "button":{
5 | "data":"one", "h":50, "bc":"#ccc", "borderWidth":1, "borderColor":"#ccc", "cornerRadius":10, "focusable":true,
6 | "events":{
7 | "load":{
8 | "actions":[
9 | { "update": {"text":{"valueOf":{"property":"data"}}} }
10 | ]
11 | },
12 | "gainedFocus":{
13 | "actions":[
14 | {
15 | "update":{
16 | "id":"focus",
17 | "x":{"valueOf":{"property":"screenX"}},
18 | "y":{"valueOf":{"property":"screenY"}},
19 | "w":{"valueOf":{"property":"w"}},
20 | "h":{"valueOf":{"property":"h"}},
21 | "opacity":1,
22 | "duration":0.4
23 | }
24 | }
25 | ]
26 | },
27 | "lostFocus":{
28 | "actions":[
29 | {
30 | "update":{
31 | "id":"focus",
32 | "opacity":0
33 | }
34 | }
35 | ]
36 | },
37 | "tapped":{
38 | "actions":[
39 | {
40 | "update":{
41 | "text":"tapped", "bc":"#eee", "duration":0.4,
42 | "events":{
43 | "completion":{
44 | "actions":[
45 | { "update": {"text":{"valueOf":{"property":"data"}}, "bc":"#ccc" } }
46 | ]
47 | }
48 | }
49 | }
50 | }
51 | ]
52 | }
53 | }
54 | }
55 | }
56 | },
57 | "pages":[
58 | {
59 | "play":"never",
60 | "elements":[
61 | { "template":"button", "data":"1", "y":50 },
62 | {
63 | "y":105, "h":"50",
64 | "elements":[
65 | { "template":"button", "data":"2", "w":"50%" },
66 | { "template":"button", "data":"3", "x":"50%", "w":"50%" }
67 | ]
68 | },
69 | {
70 | "y":160, "h":"50",
71 | "elements":[
72 | { "template":"button", "data":"4", "w":"50%" },
73 | { "template":"button", "data":"5", "x":"50%", "w":"50%" }
74 | ]
75 | },
76 | { "template":"button", "data":"6", "y":215, "x":"50%", "w":"50%" },
77 | { "template":"button", "data":"7", "y":270, "x":"50%", "w":"50%" },
78 | { "template":"button", "data":"8", "y":270, "w":"50%" },
79 | {
80 | "y":330, "h":"105", "x":0, "w":"50%",
81 | "elements":[
82 | { "template":"button", "data":"9" },
83 | { "template":"button", "data":"10", "y":55 }
84 | ]
85 | },
86 | { "id":"focus", "borderWidth":2, "borderColor":"red", "opacity":0 }
87 | ]
88 | }
89 | ]
90 | }
--------------------------------------------------------------------------------
/sample/script/tv/tv_index.swipe:
--------------------------------------------------------------------------------
1 | {
2 | "type":"net.swipe.list",
3 | "rowHeight":"10%",
4 | "items":[
5 | { "url":"focus.swipe", "title":"Focus", "icon":"Icon-180" },
6 | ]
7 | }
8 |
--------------------------------------------------------------------------------
/sample/script/update.swipe:
--------------------------------------------------------------------------------
1 | {
2 | "templates":{
3 | "elements":{
4 | "focus": {
5 | "fontSize":20,
6 | "events": {
7 | "gainedFocus":{
8 | "actions":[
9 | {
10 | "update":{
11 | "id":"focus",
12 | "x":{"valueOf":{"property":"screenX"}},
13 | "y":{"valueOf":{"property":"screenY"}},
14 | "w":{"valueOf":{"property":"w"}},
15 | "h":{"valueOf":{"property":"h"}},
16 | "opacity":1,
17 | "duration":0.4
18 | }
19 | }
20 | ]
21 | },
22 | "lostFocus":{
23 | "actions":[
24 | {
25 | "update":{
26 | "id":"focus",
27 | "opacity":0
28 | }
29 | }
30 | ]
31 | }
32 | }
33 | }
34 | }
35 | },
36 | "pages": [
37 | {
38 | "play":"never",
39 | "elements": [
40 | { "id":"caption", "text":"sample", "textColor":"#fff", "fontSize":20, "x":"1%", "w":"98%", "h":40, "y":40, "bc":"#eee" },
41 | { "text":"Tap a button to display its 'text' property above", "x":"1%", "w":"98%", "y":82, "h":60, "fontSize":18 },
42 | {
43 | "text":"one", "x":"1%", "w":"48%", "h":40, "y":140, "bc":"#fdd", "borderWidth":1, "borderColor":"#ccc", "cornerRadius":10, "template":"focus", "focusable":true,
44 | "events": {
45 | "tapped": {
46 | "actions":[{ "update":{ "id":"caption", "text":{"valueOf":{ "property":"text"} }, "textColor":"#f00", "bc":"#0f0", "duration":0.4 }}]
47 | }
48 | }
49 | },
50 | {
51 | "text":"two", "x":"51%", "w":"48%", "h":40, "y":140, "bc":"#dfd", "borderWidth":1, "borderColor":"#ccc", "cornerRadius":10, "template":"focus", "focusable":true,
52 | "events": {
53 | "tapped": {
54 | "actions":[{ "update":{ "id":"caption", "text":{"valueOf":{ "property":"text"} }, "textColor":"#0f0", "bc":"#f00", "duration":0.4 }}]
55 | }
56 | }
57 | },
58 | { "text":"Tap text field below, enter text, tap on the button to display the text field's 'text' property above", "x":"1%", "w":"98%", "y":200, "h":60, "fontSize":18 },
59 | {
60 | "id":"input", "textField":{}, "textAlign":"center", "text":"hi", "borderWidth":1, "borderColor":"#ccc", "cornerRadius":10, "template":"focus", "focusable":true,
61 | "x":"1%", "y":260, "w":"82%", "h":50, "bc":"#fff",
62 | "events":{
63 | "textChanged":{
64 | "actions":[{ "update":{ "id":"btn", "enabled":{ "valueOf":{ "property":"text.length" }}} }]
65 | }
66 | }
67 | },
68 | {
69 | "id":"btn", "text":"", "x":"84%", "y":260, "w":50, "h":50, "textColor":"#ccc", "bc":"#fff", "borderWidth":1, "borderColor":"#ccc", "cornerRadius":25, "enabled":false, "template":"focus", "focusable":true,
70 | "events":{
71 | "enabled":{
72 | "actions":[ { "update":{ "focusable":true, "text":"^", "textColor":"#1E88E5", "borderColor":"#1E88E5" } } ]
73 | },
74 | "disabled":{
75 | "actions":[ { "update":{ "focusable":false, "text":"", "textColor":"#ccc", "borderColor":"#ccc" } } ]
76 | },
77 | "tapped":{
78 | "//": "fired only if enabled",
79 | "actions":[
80 | { "update":{ "id":"caption", "text":{"valueOf":{ "id":"input", "property":"text"} }, "textColor":"#000", "bc":"#ddd", "duration":0.4 }},
81 | { "update":{ "enabled":false } },
82 | { "update":{ "id":"input", "text":"" } }
83 | ]
84 | },
85 | "load":{
86 | "actions":[{ "update":{ "enabled":{ "valueOf":{ "id":"input", "property":"text.length" }}} }]
87 | }
88 | }
89 | },
90 | { "id":"focus", "borderWidth":2, "borderColor":"red", "opacity":0 }
91 | ]
92 | }
93 | ]
94 | }
95 |
--------------------------------------------------------------------------------
/sample/script/vectors.swipe:
--------------------------------------------------------------------------------
1 | {
2 | "title": "",
3 | "scenes": {
4 | "s0": {
5 | "elements":
6 | [{
7 | "bc":"#ff000033",
8 | "fillColor": "#ffffffe0",
9 | "y": 459.5,
10 | "h": 324.5,
11 | "w": 226.5,
12 | "id": "id0",
13 | "opacity": 0,
14 | "path": "M54.5,324.5Q106.315006604422,199.846174687046,0.0,214.0 226.5,0.0,202.0,316.0 155.743702216728,211.844523128144,54.5,324.5Z",
15 | "lineWidth": 3,
16 | "scale": [2.129, 2.129],
17 | "_hidden": true,
18 | "strokeColor": "#e40000ff",
19 | "x": 141.5
20 | },
21 | {
22 | "bc":"#00ff0033",
23 | "fillColor": "#ffffffe0",
24 | "y": 160,
25 | "h": 305.3183288574219,
26 | "w": 218,
27 | "id": "id1",
28 | "opacity": 0,
29 | "path": "M100.0,176.5Q16.0068081793643,250.81683739138,89.5,82.0 0.0,119.0,30.0,59.5 60.0,0.0,109.75,17.0 159.5,34.0,188.75,71.0 218.0,108.0,207.5,139.25 197.0,170.5,159.0,112.5 165.864229617208,305.318314190719,100.0,176.5Z",
30 | "lineWidth": 4,
31 | "scale": [1.829, 1.829],
32 | "_hidden": true,
33 | "strokeColor": "#0b8700ff",
34 | "x": 241.5
35 | },
36 | {
37 | "bc":"#0000ff33",
38 | "fillColor": "#ffffffe0",
39 | "y": 318,
40 | "h": 261.5,
41 | "w": 241.5,
42 | "id": "id2",
43 | "opacity": 0,
44 | "path": "M140.5,229.5Q105.793442883693,144.207260232528,47.5,261.5 0.0,30.0,87.0,126.5 70.0,0.0,155.75,41.5 241.5,83.0,234.0,137.25 226.5,191.5,177.0,117.5 142.064997296804,86.5737682687859,140.5,229.5Z",
45 | "lineWidth": 4,
46 | "scale": [1.59, 1.59],
47 | "_hidden": true,
48 | "strokeColor": "#004087ff",
49 | "x": 70
50 | },
51 | {
52 | "shadow": {
53 | "opacity": 1,
54 | "radius": 4
55 | },
56 | "w": "90%",
57 | "id": "caption",
58 | "text": {
59 | "ref": "caption"
60 | },
61 | "opacity": 0,
62 | "x": "5%",
63 | "textColor": "#fff",
64 | "to": {
65 | "opacity": 1
66 | },
67 | "textAlign": "bottom",
68 | "y": "2.5%",
69 | "h": "95%"
70 | }
71 | ],
72 | "eyePosition": 1.5
73 | }
74 | },
75 | "dimension": [576, 1024],
76 | "languages": [],
77 |
78 | "pages": [
79 | {
80 | "elements": [{
81 | "id": "id0",
82 | "opacity": 1,
83 | "_hidden": false
84 | },
85 | {
86 | "id": "id1",
87 | "opacity": 1,
88 | "_hidden": false
89 | },
90 | {
91 | "id": "id2",
92 | "opacity": 1,
93 | "_hidden": false
94 | }
95 | ],
96 | "scene": "s0",
97 | "play": "auto"
98 | },
99 | {
100 | "elements": [{
101 | "opacity": 1,
102 | "id": "id0",
103 | "_hidden": false,
104 | "to": {
105 | "pos": "M0,0 l-153.5,46.0",
106 | "fillColor": "#ff7272a0",
107 | "translate": [ -153.5, 46],
108 | "rotate": [0, 0, -51.5]
109 | }
110 | },
111 | {
112 | "opacity": 1,
113 | "id": "id1",
114 | "_hidden": false,
115 | "to": {
116 | "pos": "M0,0 l-97.0,-51.0",
117 | "fillColor": "#a9ffa1a0",
118 | "translate": [ -97, -51],
119 | "rotate": [0, 0, -39.5]
120 | }
121 | }, {
122 | "opacity": 1,
123 | "id": "id2",
124 | "_hidden": false,
125 | "to": {
126 | "pos": "M0,0 l194.5,13.0",
127 | "fillColor": "#43d9ffa0",
128 | "translate": [194.5, 13],
129 | "rotate": [0, 0, 45]
130 | }
131 | }
132 | ],
133 | "scene": "s0",
134 | "play": "scroll"
135 | }
136 | ],
137 | "type": "net.swipe.swipe"
138 | }
--------------------------------------------------------------------------------
/sample/script/videostream.swipe:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Video Stream Test",
3 | "orientation":"landscape",
4 | "templates": {
5 | "pages":{
6 | "*":{
7 | "bc":"#333",
8 | "play":"always",
9 | },
10 | },
11 | },
12 | "pages":[
13 | {
14 | "elements":[
15 | { "video":"http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8", "stream":true },
16 | ],
17 | },
18 | {
19 | "elements":[
20 | { "video":"http://devstreaming.apple.com/videos/wwdc/2015/214dh5q5d0kswh/214/hls_vod_mvp.m3u8", "stream":true },
21 | ],
22 | },
23 | {
24 | "elements":[
25 | { "video":"https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/bipbop_4x3_variant.m3u8", "stream":true },
26 | ],
27 | },
28 | {
29 | "elements":[
30 | { "video":"https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_16x9/bipbop_16x9_variant.m3u8", "stream":true },
31 | ],
32 | },
33 | ]
34 | }
35 |
--------------------------------------------------------------------------------