├── .gitignore ├── index.js ├── package.json ├── tests └── index.html ├── README.md └── lib ├── naranja.min.css ├── naranja.less ├── naranja.min.js └── naranja.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | test 4 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import naranja from './lib/naranja' 2 | 3 | export default naranja -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "naranja", 3 | "version": "1.0.4", 4 | "description": "Notifications for modern web design", 5 | "main": "index.js", 6 | "directories": { 7 | "lib": "lib" 8 | }, 9 | "keywords": [ 10 | "notifications", 11 | "pretty-notifications", 12 | "pure-javascript-notifications", 13 | "javascript-notifications" 14 | ], 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/e1016/naranja.git" 18 | }, 19 | "author": "Eliseo Geraldo ", 20 | "license": "MIT" 21 | } 22 | -------------------------------------------------------------------------------- /tests/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Naranja notifications 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 43 | 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |

🍊 Naranja

5 | 6 |

Pure JS, HTML, and CSS Notifications with a great look – Live demo

7 | 8 | 9 | `npm install --save naranja` 10 | 11 | 12 |
Script
13 |

14 | <script src="https://unpkg.com/naranja@1.0.1/lib/naranja.min.js"></script> 15 |

16 |
Styles
17 |

18 | <link rel="stylesheet" href="https://unpkg.com/naranja@1.0.1/lib/naranja.min.css"> 19 |

20 |

npm i -s naranja

21 | 22 | 23 | ```js 24 | // script 25 | import naranja from 'naranja' 26 | // styles 27 | import '~/naranja/lib/naranja.min.css' 28 | ``` 29 |

30 | 31 |

32 | 33 | --- 34 | 35 | _For first, why naranja? ... because all cool names in npm are taken, yes, and is easy to remember (it's orange in English)._ 36 | 37 | ```js 38 | naranja().log({ 39 | title: 'Notification Title', // <- required 40 | text: 'Here goes a description for notifiaction', // <- required 41 | icon: true or false, // <- unrequired, default true, 42 | timeout: 2000 or 'keep', // <- unrequired, default 5000 miliseconds 43 | buttons: [ 44 | { 45 | text: 'OK', 46 | click: function (e) { 47 | // click event close notifiaction 48 | // unless you use preventClose method 49 | e.preventClose() 50 | // if you want close notifiaction 51 | // manually, use closeNotification 52 | e.closeNotification() 53 | } 54 | }, 55 | { 56 | text: 'Cancel', 57 | click: function () { 58 | // make something here... 59 | 60 | // you can (but you should not) 61 | // add infinity buttons 62 | } 63 | } 64 | ] 65 | }) 66 | ``` 67 | 68 | more notifiactions 69 | 70 | ```js 71 | 72 | naranja().log({ ... 73 | 74 | naranja().success({ ... 75 | 76 | naranja().warn({ ... 77 | 78 | naranja().error({ ... 79 | 80 | ``` 81 | 82 | All methods need the same arguments 83 | -------------------------------------------------------------------------------- /lib/naranja.min.css: -------------------------------------------------------------------------------- 1 | .naranja-notification{height:0;box-sizing:content-box;padding:10px 0;transition:padding .7s cubic-bezier(0, .5, 0, 1),height .7s cubic-bezier(0, .5, 0, 1)}.naranja-notification *{box-sizing:border-box}.naranja-notification .narj-log{background-color:#F9F9F9}.naranja-notification .narj-log button{border:1px solid #D2D2D2;background-color:white}.naranja-notification .narj-log button:first-of-type{color:#0099E5}.naranja-notification .narj-success{background-color:#B8F4BC}.naranja-notification .narj-success button{border:1px solid #6ED69A;background-color:#B8F4BC;opacity:.9;color:#11B674}.naranja-notification .narj-success button:first-of-type{opacity:1}.naranja-notification .narj-warn{background-color:#FFDD85}.naranja-notification .narj-warn button{border:1px solid #F5CE69;background-color:#FFDD85;opacity:.9;color:#D9993F}.naranja-notification .narj-warn button:first-of-type{opacity:1}.naranja-notification .narj-error{background-color:#ED9286}.naranja-notification .narj-error button{border:1px solid #ED8476;background-color:#ED9286;opacity:.9;color:#C24343}.naranja-notification .narj-error button:first-of-type{opacity:1}.naranja-notification .naranja-body-notification{animation:.4s fadeUpIn 1 cubic-bezier(0, .5, 0, 1);position:relative;display:flex;width:310px;border-radius:4px;padding:7px;box-shadow:0 5px 10px rgba(0,0,0,0.16);margin-bottom:7px;margin-top:12px;opacity:1;transition:opacity .15s ease,marginTop .3s ease,marginBottom .3s ease,padding .3s ease}.naranja-notification .naranja-body-notification:hover .naranja-close-icon{opacity:.7}.naranja-notification .naranja-body-notification:hover .naranja-close-icon:hover{opacity:1}.naranja-notification .naranja-body-notification>div{display:inline-flex;justify-content:center;align-items:center}.naranja-notification .naranja-body-notification .naranja-text-and-title{padding-left:15px;flex-direction:column;justify-content:center;align-items:flex-start}.naranja-notification .naranja-body-notification .naranja-text-and-title>p{margin:5px;font-family:'Open Sans'}.naranja-notification .naranja-body-notification .naranja-text-and-title>div{width:100%}.naranja-notification .naranja-body-notification .naranja-text-and-title>div button{float:right;margin-left:6px;margin-top:10px;margin-bottom:2px;-webkit-appearance:none;-moz-appearance:none;-ms-appearance:none;-o-appearance:none;border-radius:3px;padding:2px 11px;font-size:14px;text-align:center;outline:none;border-width:1px;box-shadow:0 2px 4px -2px rgba(0,0,0,0.2);cursor:pointer}.naranja-notification .naranja-body-notification .naranja-text-and-title>div button:active{opacity:.7}.naranja-notification .naranja-body-notification .naranja-title{font-size:20px;opacity:1}.naranja-notification .naranja-body-notification .naranja-parragraph{font-size:14px;opacity:.6;padding-right:30px;line-height:1.4em}.naranja-close-icon{position:absolute;right:7px;top:7px;opacity:0;cursor:pointer;transition:opacity .25s ease}@keyframes fadeUpIn{from{opacity:.2;box-shadow:0 0 0 rgba(0,0,0,0.5);transform:scale(.95)}75%{opacity:1}to{opacity:1;box-shadow:0 5px 10px rgba(0,0,0,0.16);transform:scale(1)}}.naranja-notification-box{box-sizing:content-box;display:flex;flex-direction:column-reverse;position:fixed;bottom:0;right:0;width:315px;height:auto;max-height:100vh;overflow:auto;padding:8px;padding-top:20px}.naranja-notification-box .naranja-notification-advice{position:fixed;right:138px;top:-39px;transform:translateY(0);cursor:pointer;transition:transform .3s ease}.naranja-notification-box .naranja-notification-advice.active{transform:translateY(60px)} -------------------------------------------------------------------------------- /lib/naranja.less: -------------------------------------------------------------------------------- 1 | // out: naranja.min.css, compress: true 2 | 3 | @log: #F9F9F9; 4 | @logButtonBorder: #D2D2D2; 5 | @logShadow: #000; 6 | 7 | @success: #B8F4BC; 8 | @successButtonColor: #11B674; 9 | @successButtonBorder: #6ED69A; 10 | 11 | @warn: #FFDD85; 12 | @warnButtonColor: #D9993F; 13 | @warnButtonBorder: #F5CE69; 14 | 15 | @error: #ED9286; 16 | @errorButtonColor: #C24343; 17 | @errorButtonBorder: #ED8476; 18 | 19 | .naranja-notification { 20 | height: 0px; 21 | box-sizing: content-box; 22 | padding: 10px 0; 23 | transition: padding 0.7s cubic-bezier(0, 0.5, 0, 1), height 0.7s cubic-bezier(0, 0.5, 0, 1); 24 | * { box-sizing: border-box } 25 | .narj { 26 | &-log { 27 | background-color: @log; 28 | button { 29 | border: 1px solid @logButtonBorder; 30 | background-color: white; 31 | &:first-of-type { 32 | color: #0099E5; 33 | } 34 | } 35 | } 36 | &-success { 37 | background-color: @success; 38 | button { 39 | border: 1px solid @successButtonBorder; 40 | background-color: @success; 41 | opacity: 0.9; 42 | color: @successButtonColor; 43 | &:first-of-type { 44 | opacity: 1; 45 | } 46 | } 47 | } 48 | &-warn { 49 | background-color: @warn; 50 | button { 51 | border: 1px solid @warnButtonBorder; 52 | background-color: @warn; 53 | opacity: 0.9; 54 | color: @warnButtonColor; 55 | &:first-of-type { 56 | opacity: 1; 57 | } 58 | } 59 | } 60 | &-error { 61 | background-color: @error; 62 | button { 63 | border: 1px solid @errorButtonBorder; 64 | background-color: @error; 65 | opacity: 0.9; 66 | color: @errorButtonColor; 67 | &:first-of-type { 68 | opacity: 1; 69 | } 70 | } 71 | } 72 | } 73 | 74 | .naranja-body-notification { 75 | animation: 0.4s fadeUpIn 1 cubic-bezier(0, 0.5, 0, 1); 76 | position: relative; 77 | display: flex; 78 | width: 310px; 79 | border-radius: 4px; 80 | padding: 7px; 81 | box-shadow: 0 5px 10px fade(@logShadow, 16%); 82 | margin-bottom: 7px; 83 | margin-top: 12px; 84 | opacity: 1; 85 | transition: opacity 0.15s ease, marginTop 0.3s ease, marginBottom 0.3s ease, padding 0.3s ease; 86 | &:hover { 87 | .naranja-close-icon { 88 | opacity: 0.7; 89 | &:hover { 90 | opacity: 1; 91 | } 92 | } 93 | } 94 | > div { 95 | display: inline-flex; 96 | justify-content: center; 97 | align-items: center; 98 | } 99 | .naranja-text-and-title { 100 | padding-left: 15px; 101 | flex-direction: column; 102 | justify-content: center; 103 | align-items: flex-start; 104 | > p { 105 | margin: 5px; 106 | font-family: 'Open Sans'; 107 | } 108 | > div { 109 | width: 100%; 110 | button { 111 | float: right; 112 | margin-left: 6px; 113 | margin-top: 10px; 114 | margin-bottom: 2px; 115 | -webkit-appearance: none; 116 | -moz-appearance: none; 117 | -ms-appearance: none; 118 | -o-appearance: none; 119 | 120 | border-radius: 3px; 121 | padding: 2px 11px; 122 | font-size: 14px; 123 | text-align: center; 124 | outline: none; 125 | border-width: 1px; 126 | box-shadow: 0 2px 4px -2px fade(black, 20%); 127 | cursor: pointer; 128 | &:active { 129 | opacity: 0.7; 130 | } 131 | } 132 | } 133 | } 134 | .naranja-title { 135 | font-size: 20px; 136 | opacity: 1; 137 | } 138 | .naranja-parragraph { 139 | font-size: 14px; 140 | opacity: 0.6; 141 | padding-right: 30px; 142 | line-height: 1.4em; 143 | } 144 | } 145 | } 146 | 147 | .naranja-close-icon { 148 | position: absolute; 149 | right: 7px; 150 | top: 7px; 151 | opacity: 0; 152 | cursor: pointer; 153 | transition: opacity 0.25s ease; 154 | } 155 | 156 | @keyframes fadeUpIn { 157 | from { 158 | opacity: 0.2; 159 | box-shadow: 0 0px 0px fade(@logShadow, 50%); 160 | transform: scale(0.95); 161 | } 162 | 75% { 163 | opacity: 1; 164 | } 165 | to { 166 | opacity: 1; 167 | box-shadow: 0 5px 10px fade(@logShadow, 16%); 168 | transform: scale(1); 169 | } 170 | } 171 | 172 | .naranja-notification-box { 173 | box-sizing: content-box; 174 | display: flex; 175 | flex-direction: column-reverse; 176 | position: fixed; 177 | bottom: 0; 178 | right: 0; 179 | width: 315px; 180 | height: auto; 181 | max-height: 100vh; 182 | overflow: auto; 183 | padding: 8px; 184 | padding-top: 20px; 185 | .naranja-notification-advice { 186 | position: fixed; 187 | right: 138px; 188 | top: -39px; 189 | transform: translateY(0px); 190 | cursor: pointer; 191 | transition: transform 0.3s ease; 192 | &.active { 193 | transform: translateY(60px); 194 | } 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /lib/naranja.min.js: -------------------------------------------------------------------------------- 1 | !function(t){"undefined"!=typeof module&&module.exports?module.exports=t:window.naranja=t}(function(){function t(t,i){var e=document.createElement(t);return i&&i.forEach(function(t){e.classList.add(t)}),e}var i=document.querySelector(".naranja-notification-box");return i||(i=t("div",["naranja-notification-box"]),$newNotificationsAdvice=t("div",["naranja-notification-advice"]),$newNotificationsAdvice.addEventListener("click",function(){i.scrollTop="0"}),i.appendChild($newNotificationsAdvice),document.body.appendChild(i)),i.__proto__.unshifElement=function(t){this.insertBefore(t,this.childNodes[0])},i.addEventListener("scroll",function(t){t.currentTarget.scrollTop<20&&$newNotificationsAdvice.classList.remove("active")}),{log:function(t){this.createNotification("log",t)},success:function(t){this.createNotification("success",t)},warn:function(t){this.createNotification("warn",t)},error:function(t){this.createNotification("error",t)},createNotification:function(e,o){this.type=e,this.title=o.title,this.text=o.text,this.icon=void 0===o.icon||o.icon,this.buttons=o.buttons;var n=this.$createContainer(),a=n.querySelector("div");if(this.$notification=n,this.$body=a,this.icon){var c=t("div",["naranja-icon","narj-icon-"+e]);c.innerHTML=this.chooseIcon[e],a.appendChild(c)}var r=this.createTitle(),s=this.createText(),l=t("div",["naranja-text-and-title"]);if(l.appendChild(r),l.appendChild(s),a.appendChild(l),this.buttons){var d=this.createButtons(n,a);a.querySelector(".naranja-text-and-title").appendChild(d)}var f,h=t("div",["naranja-close-icon"]);h.addEventListener("click",function(){this.closeNotification()}.bind(this)),h.innerHTML=this.chooseIcon.close,a.appendChild(h),i.unshifElement(n),f=n,setTimeout(function(){var t=f.querySelector(".naranja-body-notification").offsetHeight;f.style.height=t+"px"},0),i.scrollTop>20&&($newNotificationsAdvice.classList.add("active"),$newNotificationsAdvice.innerHTML=this.chooseIcon.newNotification),"keep"!==o.timeout&&setTimeout(function(){this.closeNotification()}.bind(this),o.timeout||5e3)},$createContainer:function(){var i=t("div",["naranja-notification","naranja-"+this.type]),e=t("div",["naranja-body-notification","narj-"+this.type]);return i.appendChild(e),i},createTitle:function(){var i,e=t("p",["naranja-title"]),o=(i=this.title,document.createTextNode(i));return e.appendChild(o),e},createText:function(){var i=t("p",["naranja-parragraph"]),e=document.createTextNode(this.text);return i.appendChild(e),i},createButtons:function(i,e){var o=t("div",["naranja-buttons-container"]),n=this;return this.buttons.forEach(function(i){var e=t("button");e.appendChild(document.createTextNode(i.text)),e.addEventListener("click",function(t){n.removeNotification=!0,t.preventClose=function(){n.removeNotification=!1},t.closeNotification=function(){n.closeNotification()},i.click(t),n.removeNotification&&n.closeNotification()}),o.appendChild(e)}),o},closeNotification:function(){var t=this;this.elementWasRemoved||(t.$body.style.opacity="0",setTimeout(function(){t.$body.style.marginTop="0px",t.$body.style.marginBottom="0px",t.$body.style.padding="0px",t.$notification.style.height="0px",t.$notification.style.padding="0px",setTimeout(function(){t.$notification.parentNode.removeChild(t.$notification)},600),i.scrollTop<20&&$newNotificationsAdvice.classList.remove("active")},150)),this.elementWasRemoved=!0},chooseIcon:{log:'',success:'',warn:'',error:'',close:'',newNotification:''}}}); 2 | -------------------------------------------------------------------------------- /lib/naranja.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | // TODO: Add bip sound 4 | // http://soundbible.com/mp3/A-Tone-His_Self-1266414414.mp3 5 | 6 | (function (factory) { 7 | 8 | // checking for exports avalible 9 | if (typeof module !== 'undefined' && module.exports) { 10 | // export Collection 11 | module.exports = factory 12 | } else { 13 | // else add to root variable 14 | window['naranja'] = factory 15 | } 16 | 17 | })(function () { 18 | 19 | function setSideUpAnimation (finalNotification) { 20 | setTimeout(function () { 21 | 22 | var notificationHeight = finalNotification 23 | .querySelector('.naranja-body-notification') 24 | .offsetHeight 25 | 26 | finalNotification.style.height = notificationHeight + 'px' 27 | }, 0) 28 | } 29 | 30 | function createText (text) { 31 | return document.createTextNode(text) 32 | } 33 | 34 | /** 35 | * provide a reusable way to create html 36 | * elements 37 | * @param {String} tag – html tag name 38 | * @param {String[]} classes – html tag classes 39 | */ 40 | 41 | function createElement (tag, classes) { 42 | var $HTMLElement = document.createElement(tag) 43 | if (!!classes) { 44 | classes.forEach(function (className) { 45 | $HTMLElement.classList.add(className) 46 | }) 47 | } 48 | 49 | return $HTMLElement 50 | } 51 | 52 | var $narjContainer = document.querySelector('.naranja-notification-box') 53 | 54 | if (!$narjContainer) { 55 | $narjContainer = createElement('div', ['naranja-notification-box']) 56 | $newNotificationsAdvice = createElement('div', ['naranja-notification-advice']) 57 | $newNotificationsAdvice.addEventListener('click', function () { 58 | $narjContainer.scrollTop = '0' 59 | }) 60 | $narjContainer.appendChild($newNotificationsAdvice) 61 | 62 | document.body.appendChild($narjContainer) 63 | } 64 | 65 | $narjContainer.__proto__.unshifElement = function (node) { 66 | this.insertBefore(node, this.childNodes[0]) 67 | } 68 | 69 | $narjContainer.addEventListener('scroll', function (e) { 70 | if (e.currentTarget.scrollTop < 20) { 71 | $newNotificationsAdvice.classList.remove('active') 72 | } 73 | }) 74 | 75 | return { 76 | log: function (argm) { 77 | this.createNotification('log', argm) 78 | }, 79 | success: function (argm) { 80 | this.createNotification('success', argm) 81 | }, 82 | warn: function (argm) { 83 | this.createNotification('warn', argm) 84 | }, 85 | error: function (argm) { 86 | this.createNotification('error', argm) 87 | }, 88 | 89 | /* 90 | * Internal methods for 91 | * launch notifications 92 | */ 93 | createNotification: function (type, argm) { 94 | 95 | this.type = type 96 | this.title = argm.title 97 | this.text = argm.text 98 | this.icon = (argm.icon === undefined) ? true : argm.icon 99 | this.buttons = argm.buttons 100 | 101 | var $notification = this.$createContainer() 102 | var $body = $notification.querySelector('div') 103 | 104 | this.$notification = $notification 105 | this.$body = $body 106 | 107 | // render icon if exists 108 | if (this.icon) { 109 | var $iconContainer = createElement('div', [ 110 | 'naranja-icon', 111 | 'narj-icon-' + type 112 | ]) 113 | 114 | $iconContainer.innerHTML = this.chooseIcon[type] 115 | 116 | $body.appendChild($iconContainer) 117 | } 118 | 119 | var $title = this.createTitle() 120 | var $text = this.createText() 121 | 122 | var $textAndTitleContainer = createElement('div', [ 123 | 'naranja-text-and-title' 124 | ]) 125 | 126 | $textAndTitleContainer.appendChild($title) 127 | $textAndTitleContainer.appendChild($text) 128 | 129 | $body.appendChild($textAndTitleContainer) 130 | 131 | // render buttons fragment if exists 132 | if (this.buttons) { 133 | var $buttons = this.createButtons($notification, $body) 134 | 135 | $body 136 | .querySelector('.naranja-text-and-title') 137 | .appendChild($buttons) 138 | } 139 | 140 | var $close = createElement('div', [ 141 | 'naranja-close-icon' 142 | ]) 143 | 144 | $close.addEventListener('click', (function () { 145 | this.closeNotification() 146 | }).bind(this)) 147 | 148 | $close.innerHTML = this.chooseIcon.close 149 | 150 | $body.appendChild($close) 151 | 152 | $narjContainer.unshifElement($notification) 153 | setSideUpAnimation($notification) 154 | 155 | if ($narjContainer.scrollTop > 20) { 156 | $newNotificationsAdvice.classList.add('active') 157 | $newNotificationsAdvice.innerHTML = this.chooseIcon.newNotification 158 | } 159 | 160 | if (argm.timeout !== 'keep') { 161 | setTimeout( 162 | (function () { 163 | this.closeNotification() 164 | }).bind(this), 165 | argm.timeout || 5000 166 | ) 167 | } 168 | }, 169 | $createContainer: function () { 170 | // generate box for notification 171 | 172 | var $container = createElement('div', [ 173 | 'naranja-notification', 174 | 'naranja-' + this.type 175 | ]) 176 | 177 | var $innerContainer = createElement('div', [ 178 | 'naranja-body-notification', 179 | 'narj-' + this.type 180 | ]) 181 | 182 | $container.appendChild($innerContainer) 183 | 184 | return $container 185 | }, 186 | createTitle: function () { 187 | var $parragraph = createElement('p', [ 188 | 'naranja-title' 189 | ]) 190 | var $tt = createText(this.title) 191 | $parragraph.appendChild($tt) 192 | 193 | return $parragraph 194 | }, 195 | createText: function () { 196 | var $title = createElement('p', [ 197 | 'naranja-parragraph' 198 | ]) 199 | 200 | var $tx = document.createTextNode(this.text) 201 | $title.appendChild($tx) 202 | 203 | return $title 204 | }, 205 | createButtons: function ($notification, $body) { 206 | var $buttonsContainer = createElement('div', [ 207 | 'naranja-buttons-container' 208 | ]) 209 | 210 | var self = this 211 | 212 | this.buttons.forEach(function (button) { 213 | var $buttonElement = createElement('button') 214 | $buttonElement.appendChild(document.createTextNode(button.text)) 215 | 216 | $buttonElement.addEventListener('click', function (event) { 217 | 218 | self.removeNotification = true 219 | event.preventClose = function () { 220 | self.removeNotification = false 221 | } 222 | 223 | event.closeNotification = function () { 224 | self.closeNotification() 225 | } 226 | 227 | button.click(event) 228 | 229 | if (self.removeNotification) self.closeNotification() 230 | }) 231 | 232 | $buttonsContainer.appendChild($buttonElement) 233 | }) 234 | 235 | return $buttonsContainer 236 | }, 237 | closeNotification: function () { 238 | var self = this 239 | if ( !this.elementWasRemoved ) { 240 | self.$body.style.opacity = '0' 241 | setTimeout(function () { 242 | self.$body.style.marginTop = '0px' 243 | self.$body.style.marginBottom = '0px' 244 | self.$body.style.padding = '0px' 245 | self.$notification.style.height = 0 + 'px' 246 | self.$notification.style.padding = 0 + 'px' 247 | setTimeout(function () { 248 | self.$notification 249 | .parentNode 250 | .removeChild( 251 | self.$notification 252 | ) 253 | }, 600); 254 | if ($narjContainer.scrollTop < 20) { 255 | $newNotificationsAdvice.classList.remove('active') 256 | } 257 | }, 150) 258 | } 259 | this.elementWasRemoved = true 260 | }, 261 | chooseIcon: { 262 | log: '', 263 | 264 | success: '', 265 | 266 | warn: '', 267 | 268 | error: '', 269 | 270 | close: '', 271 | 272 | newNotification: '' 273 | } 274 | } 275 | }) 276 | --------------------------------------------------------------------------------