├── .gitignore
├── LICENSE
├── Plugin.php
├── README.md
├── assets
└── javascript
│ └── websocket.js
├── classes
├── MessageComponent.php
└── ServerFactory.php
├── components
└── WebSocket.php
├── composer.json
├── console
└── RunCommand.php
└── updates
└── version.yaml
/.gitignore:
--------------------------------------------------------------------------------
1 | /vendor/
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Leo Cavalcante
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 all
13 | 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 THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Plugin.php:
--------------------------------------------------------------------------------
1 | registerConsoleCommand('websockets.run', 'LeoCavalcante\WebSockets\Console\RunCommand');
14 | }
15 |
16 | public function pluginDetails()
17 | {
18 | return [
19 | 'name' => 'WebSockets',
20 | 'description' => 'Provides some WebSockets features.',
21 | 'author' => 'LeoCavalcante',
22 | 'icon' => 'icon-leaf'
23 | ];
24 | }
25 |
26 | public function registerComponents()
27 | {
28 | return [
29 | 'LeoCavalcante\WebSockets\Components\WebSocket' => 'websocket',
30 | ];
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # October WebSockets
2 |
3 | Add real-time features to your OctoberCMS project.
4 |
5 | ## Usage
6 |
7 | ### Start the server
8 |
9 | ```shell
10 | php artisan websockets:run
11 | ```
12 |
13 | You can specify a `--port` if you want to, default is `8080`.
14 |
15 | ### Attach the component
16 |
17 | Add the client component to your page/layout. You can set an `uri` property if you are running on a different port. Default is `ws://localhost:8080/`.
18 |
19 | ```ini
20 | [websocket]
21 | ==
22 | ```
23 |
24 | ### The API
25 |
26 | It uses an AJAX framework-like API, is familiar for OctoberCMS developers
27 |
28 | ```html
29 | data-websocket-event="name"
30 | ```
31 | It fires up `send()` method with the specified event name.
32 |
33 | ```html
34 | data-websocket-oneventname="console.log(event)"
35 | ```
36 | It evals the informed script, just like AJAX framework with a data argument.
37 |
38 | **You are ready to rock on sockets. Build a chat app!**
39 |
40 | Don't forget to add [jQuery](http://jquery.com/) and `{% scripts %}` placeholder.
41 | ```html
42 | url = "websockets"
43 | [websocket]
44 | ==
45 |
46 |
47 |
48 |
49 | Web Sockets
50 |
51 |
52 |
53 |
54 |
58 |
59 |
60 | {% scripts %}
61 |
62 |
63 | ```
64 |
--------------------------------------------------------------------------------
/assets/javascript/websocket.js:
--------------------------------------------------------------------------------
1 | if (window.jQuery === undefined)
2 | throw new Error('The jQuery library is not loaded. The WebSockets plugin cannot be initialized.');
3 |
4 | if (window.WebSocket === undefined)
5 | throw new Error('Your browser does not support WebSockets API. The WebSockets plugin cannot be initialized.');
6 |
7 | +function ($) { "use strict";
8 | var
9 | NAMESPACE = 'websocket',
10 | TRIGGER_ATTR = NAMESPACE + '-event',
11 | LISTENER_ATTR = NAMESPACE + '-on',
12 | TRIGGER_SELECTOR = '[data-' + TRIGGER_ATTR + ']';
13 |
14 | var properties = queryStringToObject(getQueryString(), true),
15 | websocket = null;
16 |
17 | function init() {
18 | websocket = new WebSocket(properties.uri);
19 | websocket.onmessage = onMessage;
20 | }
21 |
22 | function onMessage(message) {
23 | var event = JSON.parse(message.data);
24 |
25 | if (!event.name) {
26 | throw new Error('Invalid event name.');
27 | }
28 |
29 | var attrName = LISTENER_ATTR + event.name,
30 | selector = '[data-' + attrName + ']';
31 |
32 | $(document).trigger(jQuery.Event(NAMESPACE + ':' + event.name, {payload: event.payload}));
33 |
34 | $(selector).each(function () {
35 | eval('(function(event) {' + $(this).data(attrName) + '}.call(this, event))');
36 | });
37 | }
38 |
39 | function websocketSend() {
40 | var $el = $(this),
41 | $form = $el.closest('form'),
42 | data = queryStringToObject($form.serialize()),
43 | eventName = $el.data(TRIGGER_ATTR);
44 |
45 | var event = {
46 | name: eventName,
47 | payload: data
48 | };
49 |
50 | websocket.send(JSON.stringify(event));
51 | }
52 |
53 | $.fn.websocketSend = websocketSend;
54 |
55 | $(document).on('submit', TRIGGER_SELECTOR, function (event) {
56 | $(this).websocketSend();
57 | event.preventDefault();
58 | });
59 |
60 | function queryStringToObject(queryString, decode) {
61 | var query = queryString.split('&'),
62 | obj = {};
63 |
64 | for (var i = 0, l = query.length; i < l; i++) {
65 | var keyVal = query[i].split('=');
66 | obj[keyVal[0]] = decode ? decodeURIComponent(keyVal[1]) : keyVal[1];
67 | }
68 |
69 | return obj;
70 | }
71 |
72 | function getQueryString() {
73 | var scriptTags = document.getElementsByTagName('script');
74 | return scriptTags[scriptTags.length - 1].src.split('?')[1];
75 | }
76 |
77 | init();
78 | }(jQuery);
79 |
--------------------------------------------------------------------------------
/classes/MessageComponent.php:
--------------------------------------------------------------------------------
1 | peers = new \SplObjectStorage;
13 | }
14 |
15 | public function onOpen(ConnectionInterface $conn)
16 | {
17 | $this->peers->attach($conn);
18 | }
19 |
20 | public function onMessage(ConnectionInterface $from, $message)
21 | {
22 | $event = json_decode($message);
23 |
24 | foreach ($this->peers as $peer) {
25 | if ($peer == $from && !empty($event->broadcast)) {
26 | continue;
27 | }
28 |
29 | $peer->send($message);
30 | }
31 | }
32 |
33 | public function onClose(ConnectionInterface $conn)
34 | {
35 | $this->peers->detach($conn);
36 | }
37 |
38 | public function onError(ConnectionInterface $conn, \Exception $e)
39 | {
40 | $conn->close();
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/classes/ServerFactory.php:
--------------------------------------------------------------------------------
1 | 'WebSocket Component',
12 | 'description' => 'WebSocket client.'
13 | ];
14 | }
15 |
16 | public function defineProperties()
17 | {
18 | return [
19 | 'uri' => [
20 | 'title' => 'URI',
21 | 'description' => 'WebSocket server URI',
22 | 'default' => 'ws://localhost:8080/',
23 | 'type' => 'string',
24 | 'validationPattern' => '^ws\:\/\/.*',
25 | 'validationMessage' => 'Enter a valid WebSocket URI'
26 | ]
27 | ];
28 | }
29 |
30 | public function onRun()
31 | {
32 | $props = $this->getProperties();
33 | $this->addJs('/plugins/leocavalcante/websockets/assets/javascript/websocket.js?'.http_build_query($props));
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "leocavalcante/oc-websockets",
3 | "description": "Add real-time features to your OctoberCMS app",
4 | "type": "library",
5 | "require": {
6 | "cboden/ratchet": "^0.3.5"
7 | },
8 | "license": "MIT",
9 | "authors": [
10 | {
11 | "name": "leocavalcante",
12 | "email": "lc@leocavalcante.com"
13 | }
14 | ],
15 | "minimum-stability": "dev"
16 | }
17 |
--------------------------------------------------------------------------------
/console/RunCommand.php:
--------------------------------------------------------------------------------
1 | option('port');
27 | $this->info("Server listening on $port");
28 | ServerFactory::create($port)->run();
29 | }
30 |
31 | /**
32 | * Get the console command arguments.
33 | * @return array
34 | */
35 | protected function getArguments()
36 | {
37 | return [];
38 | }
39 |
40 | /**
41 | * Get the console command options.
42 | * @return array
43 | */
44 | protected function getOptions()
45 | {
46 | return [
47 | ['port', null, InputOption::VALUE_OPTIONAL, 'WS server port.', 8080],
48 | ];
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/updates/version.yaml:
--------------------------------------------------------------------------------
1 | 0.0.1: Work in Progress
2 | 0.1.0: API changes
3 | 0.2.0: Updated how dependencies are required
4 | 0.2.1: Update to new handle API method
5 |
--------------------------------------------------------------------------------