" : "
"
13 | return "\(tag)\(value ?? "")
"
14 | }
15 |
16 | override func setUp() {
17 | super.setUp()
18 |
19 | editor = SQTextEditorView()
20 | editor.delegate = self
21 |
22 | let exp = expectation(description: "\(#function)\(#line)")
23 |
24 | editorDidLoadHandler = {
25 | exp.fulfill()
26 | }
27 |
28 | waitForExpectations(timeout: timeout, handler: nil)
29 | }
30 |
31 | override func tearDown() {
32 | editor = nil
33 |
34 | super.tearDown()
35 | }
36 |
37 | func testGetHTML() {
38 | let exp = expectation(description: "\(#function)\(#line)")
39 |
40 | editor.getHTML(completion: { html in
41 |
42 | XCTAssert(html == self.makeTestHTML())
43 |
44 | exp.fulfill()
45 | })
46 |
47 | waitForExpectations(timeout: timeout, handler: nil)
48 | }
49 |
50 | func testInsertHTML() {
51 | let exp = expectation(description: "\(#function)\(#line)")
52 |
53 | let testHtml = makeTestHTML(value: "test")
54 |
55 | editor.insertHTML(testHtml) { error in
56 | XCTAssertNil(error)
57 |
58 | self.editor.getHTML(completion: { html in
59 | XCTAssert(html == testHtml)
60 | exp.fulfill()
61 | })
62 | }
63 |
64 | waitForExpectations(timeout: timeout, handler: nil)
65 | }
66 |
67 | func testSetSelectionInSameElementId() {
68 | let exp = expectation(description: "\(#function)\(#line)")
69 |
70 | let value = "test text"
71 | let tagId = "test"
72 | let testHtml = makeTestHTML(id: tagId, value: value)
73 | let startIndex = 0
74 | let endIndex = 2
75 |
76 | editor.insertHTML(testHtml) { error in
77 | XCTAssertNil(error)
78 |
79 | self.editor.setTextSelection(startElementId: tagId,
80 | startIndex: startIndex,
81 | endElementId: tagId,
82 | endIndex: endIndex,
83 | completion: { error in
84 | XCTAssertNil(error)
85 |
86 | self.editor.getSelectedText { text in
87 | XCTAssert((text ?? "") == value.prefix(endIndex - startIndex))
88 | exp.fulfill()
89 | }
90 | })
91 | }
92 |
93 | waitForExpectations(timeout: timeout, handler: nil)
94 | }
95 |
96 | func testSetSelectionCrossElementId() {
97 | let exp = expectation(description: "\(#function)\(#line)")
98 |
99 | let tagId1 = "a"
100 | let tagId2 = "b"
101 |
102 | let testHtml = "
123
456
"
103 |
104 | editor.insertHTML(testHtml) { error in
105 | XCTAssertNil(error)
106 |
107 | self.editor.setTextSelection(startElementId: tagId1, startIndex: 2, endElementId: tagId2, endIndex: 1, completion: { error in
108 | XCTAssertNil(error)
109 |
110 | self.editor.getSelectedText { text in
111 | XCTAssert((text ?? "")
112 | .replacingOccurrences(of: "\n", with: "")
113 | .replacingOccurrences(of: " ", with: "")
114 | == "34")
115 | exp.fulfill()
116 | }
117 | })
118 | }
119 |
120 | waitForExpectations(timeout: timeout, handler: nil)
121 | }
122 |
123 | func testBold() {
124 | let exp = expectation(description: "\(#function)\(#line)")
125 |
126 | let tagId = "testBold"
127 | let testValue = "123"
128 | let testHtml = makeTestHTML(id: tagId, value: testValue)
129 |
130 | editor.insertHTML(testHtml) { error in
131 | XCTAssertNil(error)
132 |
133 | self.editor.setTextSelection(startElementId: tagId, startIndex: 0, endElementId: tagId, endIndex: 2, completion: { error in
134 | XCTAssertNil(error)
135 |
136 | self.editor.bold { error in
137 | sleep(1)
138 | XCTAssertNil(error)
139 | XCTAssert(self.editor.selectedTextAttribute.format.hasBold)
140 | exp.fulfill()
141 | }
142 | })
143 | }
144 |
145 | waitForExpectations(timeout: timeout, handler: nil)
146 | }
147 |
148 | func testItalic() {
149 | let exp = expectation(description: "\(#function)\(#line)")
150 |
151 | let tagId = "test"
152 |
153 | let testHtml = "
123
"
154 |
155 | editor.insertHTML(testHtml) { error in
156 | XCTAssertNil(error)
157 |
158 | self.editor.setTextSelection(startElementId: tagId, startIndex: 0, endElementId: tagId, endIndex: 2, completion: { error in
159 | XCTAssertNil(error)
160 |
161 | self.editor.italic { error in
162 | sleep(1)
163 | XCTAssertNil(error)
164 | XCTAssert(self.editor.selectedTextAttribute.format.hasItalic)
165 | exp.fulfill()
166 | }
167 | })
168 | }
169 |
170 | waitForExpectations(timeout: timeout, handler: nil)
171 | }
172 |
173 | func testUnderline() {
174 | let exp = expectation(description: "\(#function)\(#line)")
175 |
176 | let tagId = "testUnderline"
177 |
178 | let testHtml = "
123
"
179 |
180 | editor.insertHTML(testHtml) { error in
181 | XCTAssertNil(error)
182 |
183 | self.editor.setTextSelection(startElementId: tagId, startIndex: 0, endElementId: tagId, endIndex: 2, completion: { error in
184 | XCTAssertNil(error)
185 |
186 | self.editor.underline { error in
187 | sleep(1)
188 | XCTAssertNil(error)
189 | XCTAssert(self.editor.selectedTextAttribute.format.hasUnderline)
190 | exp.fulfill()
191 | }
192 | })
193 | }
194 |
195 | waitForExpectations(timeout: timeout, handler: nil)
196 | }
197 |
198 | func testStrikethrough() {
199 | let exp = expectation(description: "\(#function)\(#line)")
200 |
201 | let tagId = "testStrikethrough"
202 |
203 | let testHtml = "
123
"
204 |
205 | editor.insertHTML(testHtml) { error in
206 | XCTAssertNil(error)
207 |
208 | self.editor.setTextSelection(startElementId: tagId, startIndex: 0, endElementId: tagId, endIndex: 2, completion: { error in
209 | XCTAssertNil(error)
210 |
211 | self.editor.strikethrough { error in
212 | sleep(1)
213 | XCTAssertNil(error)
214 | XCTAssert(self.editor.selectedTextAttribute.format.hasStrikethrough)
215 | exp.fulfill()
216 | }
217 | })
218 | }
219 |
220 | waitForExpectations(timeout: timeout, handler: nil)
221 | }
222 |
223 | func testSetTextSize() {
224 | let exp = expectation(description: "\(#function)\(#line)")
225 |
226 | let tagId = "testSetTextSize"
227 |
228 | let testHtml = "
123
"
229 |
230 | editor.insertHTML(testHtml) { error in
231 | XCTAssertNil(error)
232 |
233 | self.editor.setTextSelection(startElementId: tagId, startIndex: 0, endElementId: tagId, endIndex: 2, completion: { error in
234 | XCTAssertNil(error)
235 |
236 | let size = 25
237 |
238 | self.editor.setText(size: size, completion: { error in
239 | sleep(1)
240 | XCTAssert(self.editor.selectedTextAttribute.textInfo.size == size)
241 | exp.fulfill()
242 | })
243 | })
244 | }
245 |
246 | waitForExpectations(timeout: timeout, handler: nil)
247 | }
248 |
249 | func testSetTextColor() {
250 | let exp = expectation(description: "\(#function)\(#line)")
251 |
252 | let tagId = "testSetTextColor"
253 | let testHtml = makeTestHTML(id: tagId, value: "test")
254 |
255 | editor.insertHTML(testHtml) { error in
256 | XCTAssertNil(error)
257 |
258 | self.editor.setTextSelection(startElementId: tagId,
259 | startIndex: 0,
260 | endElementId: tagId,
261 | endIndex: 2,
262 | completion: { error in
263 | XCTAssertNil(error)
264 |
265 | let color = UIColor.brown
266 |
267 | self.editor.setText(color: color, completion: { error in
268 | sleep(1)
269 | XCTAssert(self.editor.selectedTextAttribute.textInfo.color == color)
270 | exp.fulfill()
271 | })
272 | })
273 | }
274 |
275 | waitForExpectations(timeout: timeout, handler: nil)
276 | }
277 |
278 | func testSetTextBackgroundColor() {
279 | let exp = expectation(description: "\(#function)\(#line)")
280 |
281 | let tagId = "testSetTextBackgroundColor"
282 | let testHtml = makeTestHTML(id: tagId, value: "test")
283 |
284 | editor.insertHTML(testHtml) { error in
285 | XCTAssertNil(error)
286 |
287 | self.editor.setTextSelection(startElementId: tagId,
288 | startIndex: 0,
289 | endElementId: tagId,
290 | endIndex: 2,
291 | completion: { error in
292 | XCTAssertNil(error)
293 |
294 | let color = UIColor.brown
295 |
296 | self.editor.setText(backgroundColor: color, completion: { error in
297 | sleep(1)
298 | XCTAssert(self.editor.selectedTextAttribute.textInfo.backgroundColor == color)
299 | exp.fulfill()
300 | })
301 | })
302 | }
303 |
304 | waitForExpectations(timeout: timeout, handler: nil)
305 | }
306 |
307 | func testInsertImage() {
308 | let exp = expectation(description: "\(#function)\(#line)")
309 |
310 | editor.insertImage(url: logoImgUrl, completion: { error in
311 | XCTAssertNil(error)
312 |
313 | self.editor.getHTML { html in
314 | XCTAssertNotNil(html)
315 |
316 | if let html = html {
317 | XCTAssert(html == "
\")
")
318 | }
319 |
320 | exp.fulfill()
321 | }
322 | })
323 |
324 | waitForExpectations(timeout: timeout, handler: nil)
325 | }
326 |
327 | func testMakeLink() {
328 | let exp = expectation(description: "\(#function)\(#line)")
329 |
330 | let tagId = "logo"
331 | let value = "123"
332 | let testHtml = makeTestHTML(id: tagId, value: value)
333 |
334 | editor.insertHTML(testHtml) { error in
335 | XCTAssertNil(error)
336 |
337 | self.editor.setTextSelection(startElementId: tagId,
338 | startIndex: 0,
339 | endElementId: tagId,
340 | endIndex: value.count,
341 | completion: { error in
342 | XCTAssertNil(error)
343 |
344 | self.editor.makeLink(url: self.logoImgUrl, completion: { error in
345 | XCTAssertNil(error)
346 |
347 | self.editor.getHTML { html in
348 | XCTAssertNotNil(html)
349 |
350 | if let html = html {
351 | XCTAssert(html == "
")
352 | }
353 |
354 | exp.fulfill()
355 | }
356 | })
357 |
358 | })
359 | }
360 |
361 | waitForExpectations(timeout: timeout, handler: nil)
362 | }
363 |
364 | func testRemoveLink() {
365 | let exp = expectation(description: "\(#function)\(#line)")
366 |
367 | let tagId = "testRemoveLink"
368 | let value = "123"
369 | let testHtml = makeTestHTML(id: tagId, value: value)
370 |
371 | editor.insertHTML(testHtml) { error in
372 | XCTAssertNil(error)
373 |
374 | self.editor.setTextSelection(startElementId: tagId,
375 | startIndex: 0,
376 | endElementId: tagId,
377 | endIndex: value.count,
378 | completion: { error in
379 | XCTAssertNil(error)
380 |
381 | self.editor.makeLink(url: self.logoImgUrl, completion: { error in
382 | XCTAssertNil(error)
383 |
384 | self.editor.removeLink { error in
385 | XCTAssertNil(error)
386 |
387 | self.editor.getHTML { html in
388 | XCTAssertNotNil(html)
389 |
390 | if let html = html {
391 | XCTAssert(html == "
\(value)
")
392 | }
393 |
394 | exp.fulfill()
395 | }
396 | }
397 | })
398 |
399 | })
400 | }
401 |
402 | waitForExpectations(timeout: timeout, handler: nil)
403 | }
404 |
405 | func testClearEditor() {
406 | let exp = expectation(description: "\(#function)\(#line)")
407 |
408 | let tagId = "testClearEditor"
409 | let value = "123"
410 | let testHtml = makeTestHTML(id: tagId, value: value)
411 |
412 | editor.insertHTML(testHtml) { error in
413 | XCTAssertNil(error)
414 |
415 | self.editor.clear { error in
416 | XCTAssertNil(error)
417 |
418 | self.editor.getHTML { html in
419 | XCTAssertNotNil(html)
420 |
421 | if let html = html {
422 | XCTAssert(html == self.makeTestHTML())
423 | }
424 |
425 | exp.fulfill()
426 | }
427 | }
428 | }
429 |
430 | waitForExpectations(timeout: timeout, handler: nil)
431 | }
432 |
433 | //MARK: - SQTextEditorDelegate
434 |
435 | func editorDidLoad(_ editor: SQTextEditorView) {
436 | editorDidLoadHandler?()
437 | }
438 | }
439 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2019 conscientiousness
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | 
3 |
4 | [](https://travis-ci.org/OneupNetwork/SQRichTextEditor)
5 | [](https://cocoapods.org/pods/SQRichTextEditor)
6 | [](https://cocoapods.org/pods/SQRichTextEditor)
7 | [](https://cocoapods.org/pods/SQRichTextEditor)
8 | [](https://git.zsinfo.nl/Zandor300/GeneralToolsFramework)
9 |
10 | ## Introduction
11 | I was looking for a WYSIWYG text editor for iOS and found some solutions but all of them didn't use `WKWebView`. Apple will stop accepting submissions of apps that use UIWebView [APIs](https://developer.apple.com/documentation/uikit/uiwebview). I found a [HTML5 rich text editor](https://github.com/neilj/Squire), which provides powerful cross-browser normalization in a flexible lightweight package. So I built this project and an iOS [bridge](https://github.com/OneupNetwork/Squire-native-bridge) for sending messages between Swift and JavaScript in WKWebView.
12 |
13 | ## Example
14 |
15 | To run the example project, clone the repo, and open `SQRichTextEditor.xcworkspace` from the Example directory.
16 |
17 | 
18 | 
19 |
20 | ## Requirements
21 |
22 | - Deployment target iOS 10.0+
23 | - Swift 5+
24 | - Xcode 11+
25 |
26 | ## Installation
27 |
28 | SQRichTextEditor is available through [CocoaPods](https://cocoapods.org). To install
29 | it, simply add the following line to your Podfile:
30 |
31 | ```ruby
32 | pod 'SQRichTextEditor'
33 | ```
34 |
35 | ## Features
36 |
37 | - [x] Bold
38 | - [x] Italic
39 | - [x] Underline
40 | - [x] Strikethrough
41 | - [x] Insert Image
42 | - [x] Insert HTML
43 | - [x] Make Link
44 | - [x] Text Color
45 | - [x] Text Size
46 | - [x] Text Background Color
47 |
48 | ## Getting Started
49 | The `SQTextEditorView` is a plain UIView subclass, so you are free to use it wherever you want.
50 |
51 | ```swift
52 | import SQRichTextEditor
53 |
54 | private lazy var editorView: SQTextEditorView = {
55 | /// You can pass the custom css string, if you want to change the default editor style
56 | /// var customCss: String?
57 | /// if let cssURL = Bundle.main.url(forResource: isDarkMode ? "editor_dark" : "editor_light", withExtension: "css"),
58 | /// let css = try? String(contentsOf: cssURL, encoding: .utf8) {
59 | /// customCss = css
60 | /// }
61 | /// let _editorView = SQTextEditorView(customCss: customCss)
62 |
63 | let _editorView = SQTextEditorView()
64 | _editorView.translatesAutoresizingMaskIntoConstraints = false
65 | return _editorView
66 | }()
67 |
68 | view.addSubview(editorView)
69 | editorView.topAnchor.constraint(equalTo: collectionView.bottomAnchor, constant: 10).isActive = true
70 | editorView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 10).isActive = true
71 | editorView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -10).isActive = true
72 | editorView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -10).isActive = true
73 |
74 | ```
75 |
76 | ### Delegate
77 |
78 | You can check events by implement SQTextEditorDelegate:
79 |
80 | ```swift
81 | editorView.delegate = self
82 | ```
83 |
84 | Delegate has these functions:
85 |
86 | ```swift
87 | //Called when the editor components is ready.
88 | optional func editorDidLoad(_ editor: SQTextEditorView)
89 |
90 | //Called when the user selected some text or moved the cursor to a different position.
91 | optional func editor(_ editor: SQTextEditorView,
92 | selectedTextAttributeDidChange attribute: SQTextAttribute)
93 |
94 | //Called when the user inserted, deleted or changed the style of some text.
95 | optional func editor(_ editor: SQTextEditorView,
96 | contentHeightDidChange height: Int)
97 |
98 | //Called when the user tapped the editor
99 | optional func editorDidFocus(_ editor: SQTextEditorView)
100 |
101 | //Called when the user tapped the done button of keyboard tool bar
102 | optional func editorDidTapDoneButton(_ editor: SQTextEditorView)
103 |
104 | //Called when the editor cursor moved
105 | optional func editor(_ editor: SQTextEditorView, cursorPositionDidChange position: SQEditorCursorPosition)
106 | ```
107 |
108 | ## Editor Functions
109 |
110 | ### getHTML
111 | Returns the HTML value of the editor in its current state.
112 |
113 | ```swift
114 | func getHTML(completion: @escaping (_ html: String?) -> ())
115 | ```
116 |
117 | ### insertHTML
118 | Inserts an HTML fragment at the current cursor location, or replaces the selection if selected. The value supplied should not contain tags or anything outside of that. A block to invoke when script evaluation completes or fails.
119 |
120 |
121 | ```swift
122 | func insertHTML(_ html: String, completion: ((_ error: Error?) -> ())? = nil)
123 | ```
124 |
125 | ### getSelectedText
126 | The text currently selected in the editor.
127 |
128 |
129 | ```swift
130 | func getSelectedText(completion: @escaping (_ text: String?) -> ())
131 | ```
132 |
133 | ### bold
134 | Makes any non-bold currently selected text bold (by wrapping it in a 'b' tag), otherwise removes any bold formatting from the selected text. A block to invoke when script evaluation completes or fails.
135 |
136 |
137 | ```swift
138 | func bold(completion: ((_ error: Error?) -> ())? = nil)
139 | ```
140 |
141 | ### italic
142 | By wrapping it in an 'i' tag.
143 |
144 | ```swift
145 | func italic(completion: ((_ error: Error?) -> ())? = nil)
146 | ```
147 |
148 | ### underline
149 | By wrapping it in an 'u' tag.
150 |
151 | ```swift
152 | func underline(completion: ((_ error: Error?) -> ())? = nil)
153 | ```
154 |
155 | ### strikethrough
156 | By wrapping it in a 'del' tag.
157 |
158 | ```swift
159 | func strikethrough(completion: ((_ error: Error?) -> ())? = nil)
160 | ```
161 |
162 | ### setTextColor
163 | Sets the color of the selected text.
164 |
165 | ```swift
166 | func setText(color: UIColor, completion: ((_ error: Error?) -> ())? = nil)
167 | ```
168 |
169 | ### setTextBackgroundColor
170 | Sets the color of the background of the selected text.
171 |
172 | ```swift
173 | func setText(backgroundColor: UIColor, completion: ((_ error: Error?) -> ())? = nil)
174 | ```
175 |
176 | ### setTextSize
177 | Sets the font size for the selected text.
178 |
179 | ```swift
180 | func setText(size: Int, completion: ((_ error: Error?) -> ())? = nil)
181 | ```
182 |
183 | ### insertImage
184 | Inserts an image at the current cursor location. A block to invoke when script evaluation completes or fails.
185 |
186 | ```swift
187 | func insertImage(url: String, completion: ((_ error: Error?) -> ())? = nil)
188 | ```
189 |
190 | ### makeLink
191 | Makes the currently selected text a link. If no text is selected, the URL or email will be inserted as text at the current cursor point and made into a link. A block to invoke when script evaluation completes or fails.
192 |
193 | ```swift
194 | func makeLink(url: String, completion: ((_ error: Error?) -> ())? = nil)
195 | ```
196 |
197 | ### removeLink
198 | Removes any link that is currently at least partially selected. A block to invoke when script evaluation completes or fails.
199 |
200 | ```swift
201 | func removeLink(completion: ((_ error: Error?) -> ())? = nil)
202 | ```
203 |
204 | ### clear
205 | Clear Editor's content. The method removes all Blocks and inserts new initial empty Block
206 | `
`. A block to invoke when script evaluation completes or fails.
207 |
208 | ```swift
209 | func clear(completion: ((_ error: Error?) -> ())? = nil)
210 | ```
211 |
212 | ### focus
213 | The editor gained focus or lost focus.
214 |
215 | ```swift
216 | func focus(_ isFocused: Bool, completion: ((_ error: Error?) -> ())? = nil)
217 | ```
218 |
219 | ## Contributions
220 |
221 | `SQRichTextEditor` welcomes both fixes, improvements, and feature additions. If you'd like to contribute, open a pull request with a detailed description of your changes.
222 |
223 | If you'd like to fix or add some functions for `editor.js` or `editor.css`, you can open a pull request in this [repo](https://github.com/OneupNetwork/Squire-native-bridge).
224 |
225 | ## Author
226 |
227 | Yuwei Lin, jesse@gamer.com.tw @ [OneupNetwork](https://www.gamer.com.tw/)
228 |
229 | ## License
230 |
231 | SQRichTextEditor is available under the MIT license. See the LICENSE file for more info.
232 |
233 |
234 | ## Attribution
235 |
236 | `SQRichTextEditor` uses portions of code from the following powerful source:
237 |
238 | | Component | Description | License |
239 | | :------------ | :------------ | :------------ |
240 | | [Squire](https://github.com/neilj/Squire) | Squire is an HTML5 rich text editor, which provides powerful cross-browser normalisation in a flexible lightweight package. | [MIT](https://github.com/neilj/Squire/blob/master/LICENSE) |
241 | | [EFColorPicker](https://github.com/EFPrefix/EFColorPicker) | A lightweight color picker in Swift. | [MIT](https://github.com/EFPrefix/EFColorPicker/blob/master/LICENSE) |
242 |
--------------------------------------------------------------------------------
/SQRichTextEditor.podspec:
--------------------------------------------------------------------------------
1 | #
2 | # Be sure to run `pod lib lint SQRichTextEditor.podspec' to ensure this is a
3 | # valid spec before submitting.
4 | #
5 | # Any lines starting with a # are optional, but their use is encouraged
6 | # To learn more about a Podspec see https://guides.cocoapods.org/syntax/podspec.html
7 | #
8 |
9 | Pod::Spec.new do |s|
10 | s.name = 'SQRichTextEditor'
11 | s.version = '1.0.2'
12 | s.summary = 'A rich text WYSIWYG editor for iOS base on Squire.'
13 | s.description = "A rich text WYSIWYG editor for iOS, which is based on Squire using HTML5 and javascript."
14 | s.homepage = 'https://github.com/OneupNetwork/SQRichTextEditor'
15 |
16 | s.license = { :type => 'MIT', :file => 'LICENSE' }
17 | s.author = { 'Yuwei Lin' => 'jesse@gamer.com.tw' }
18 | s.source = { :git => 'https://github.com/OneupNetwork/SQRichTextEditor.git', :tag => s.version.to_s }
19 |
20 | s.ios.deployment_target = '10.0'
21 |
22 | s.ios.source_files = 'SQRichTextEditor/Classes/*'
23 |
24 | s.ios.resources = "SQRichTextEditor/Assets/Editor/*"
25 |
26 | #s.ios.resource_bundles = { 'imageResource' => ['SQRichTextEditor/Assets/*.xcassets'] }
27 |
28 | s.ios.frameworks = 'UIKit', 'WebKit'
29 |
30 | s.swift_version = '5.1'
31 | end
32 |
--------------------------------------------------------------------------------
/SQRichTextEditor/Assets/Editor/editor.css:
--------------------------------------------------------------------------------
1 | html{height:100%}body{margin:0;padding:0;border:0;height:100%}#outer-container{min-height:100%}#editor{font-family:-apple-system,BlinkMacSystemFont,sans-serif;min-height:0;padding:0;background:0 0;cursor:text}#editor:focus{outline:0}img{max-height:100%;max-width:100%;width:auto;height:auto}h1{font-size:1.95em}h2{font-size:123.1%}h3{font-size:108%}h1,h2,h3,p{margin:1em 0}h4,h5,h6{margin:0}ol,ul{margin:0 1em;padding:0 1em}blockquote{margin:0;padding:0 10px}pre{white-space:pre-wrap;word-wrap:break-word;overflow-wrap:break-word;border-radius:3px;border:1px solid #ccc;padding:7px 10px;background:#f6f6f6;font-family:menlo,consolas,monospace;font-size:90%}code{border-radius:3px;border:1px solid #ccc;padding:1px 3px;background:#f6f6f6;font-family:menlo,consolas,monospace;font-size:90%}
--------------------------------------------------------------------------------
/SQRichTextEditor/Assets/Editor/editor.js:
--------------------------------------------------------------------------------
1 | "use strict";const container=document.getElementById("editor"),editor=new Squire(container,{blockTag:"div",tagAttributes:{a:{target:"_blank"}}}),tagName={bold:"b",italic:"i",strikethrough:"del",underline:"u",link:"a"};var lastFontInfo={},newFontInfo={},lastFormat={bold:!1,italic:!1,strikethrough:!1,underline:!1,link:!1},newFormat=clone(lastFormat),lastHeight=0,newHeight=0,isFocused=!1;function detectCursorPosition(){let t=editor.getCursorPosition();postCursorPosition({top:t.top,right:t.right,bottom:t.bottom,left:t.left,width:t.width,height:t.height,x:t.x,y:t.y})}function detectFontInfoChnaged(){let t=editor.getFontInfo().size;null!=t&&(t=t.replace("px","")),newFontInfo={color:rgbToHex(editor.getFontInfo().color),backgroundColor:rgbToHex(editor.getFontInfo().backgroundColor),family:editor.getFontInfo().family,size:t},isEquivalent(lastFontInfo,newFontInfo)||postFontInfo(lastFontInfo=clone(newFontInfo))}function detectFormatChnaged(){let t=Object.getOwnPropertyNames(lastFormat);for(let e=0;e
"),editor.moveCursorToStart()}function makeLink(t){editor.makeLink(t)}function removeLink(){editor.removeLink()}function setTextSelection(t,e,o,n){let r=Array.prototype.find.call(document.getElementById(t).childNodes,(function(t){return t.nodeType==Node.TEXT_NODE})),i=Array.prototype.find.call(document.getElementById(o).childNodes,(function(t){return t.nodeType==Node.TEXT_NODE})),s=editor.createRange(r,e,i,n);editor.setSelection(s)}function getSelectedText(){return editor.getSelectedText()}function setTextBackgroundColor(t){editor.setHighlightColour(t)}function getEditorHeight(){return container.clientHeight}function postFontInfo(t){window.webkit.messageHandlers.fontInfo.postMessage(t)}function postFormat(t){window.webkit.messageHandlers.format.postMessage(t)}function postFocusStatus(t){window.webkit.messageHandlers.isFocused.postMessage(t)}function postCursorPosition(t){window.webkit.messageHandlers.cursorPosition.postMessage(t)}document.getElementById("outer-container").onclick=function(t){t.target===this&&(isFocused||editor.focus())},editor.addEventListener("focus",(function(){postFocusStatus(isFocused=!0)}),!1),editor.addEventListener("blur",(function(){postFocusStatus(isFocused=!1)}),!1),editor.addEventListener("input",(function(){detectFontInfoChnaged(),detectFormatChnaged()}),!1),editor.addEventListener("select",(function(){detectFontInfoChnaged(),detectFormatChnaged()}),!1),editor.addEventListener("cursor",(function(){detectFontInfoChnaged(),detectFormatChnaged(),detectCursorPosition()}),!1);
--------------------------------------------------------------------------------
/SQRichTextEditor/Assets/Editor/index.html:
--------------------------------------------------------------------------------
1 |