├── .gitmodules ├── MessageCenter ├── README.md ├── controllers │ ├── type │ │ ├── detail.map.js │ │ ├── detail.text.js │ │ └── detail.url.js │ └── widget.js ├── styles │ ├── type │ │ └── detail.text.tss │ └── widget.tss ├── views │ ├── type │ │ ├── detail.map.xml │ │ ├── detail.text.xml │ │ └── detail.url.xml │ └── widget.xml └── widget.json ├── README.md └── starrating ├── README.md ├── assets ├── star.png ├── star_half.png └── star_off.png ├── controllers ├── star.js └── widget.js ├── starrating.png ├── styles ├── star.tss └── widget.tss ├── views ├── star.xml └── widget.xml └── widget.json /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "ACS-login"] 2 | path = ACS-login 3 | url = git://github.com/bettytran/acsloginwidget.git 4 | [submodule "iconicLabel"] 5 | path = iconicLabel 6 | url = git://github.com/hoyo/net.hoyohoyo.iconiclabel.git 7 | 8 | [submodule "drawermenu"] 9 | path = drawermenu 10 | url = https://github.com/ricardoalcocer/alloy-widget-drawermenu 11 | [submodule "net.hoyohoyo.zuruiline"] 12 | path = net.hoyohoyo.zuruiline 13 | url = git://github.com/hoyo/net.hoyohoyo.zuruiline.git 14 | [submodule "nl.fokkezb.tweetsView"] 15 | path = nl.fokkezb.tweetsView 16 | url = git://github.com/FokkeZB/nl.fokkezb.tweetsView.git 17 | [submodule "nl.fokkezb.browserView"] 18 | path = nl.fokkezb.browserView 19 | url = git://github.com/FokkeZB/nl.fokkezb.browserView.git 20 | [submodule "titanium_alloy_calendar"] 21 | path = titanium_alloy_calendar 22 | url = git://github.com/hamasyou/titanium_alloy_calendar.git 23 | [submodule "alloy-rss-widget"] 24 | path = alloy-rss-widget 25 | url = git://github.com/eqhes/alloy-rss-widget.git 26 | [submodule "ci-alloy-twitter-widget"] 27 | path = ci-alloy-twitter-widget 28 | url = git://github.com/aaronksaunders/ci-alloy-twitter-widget.git 29 | [submodule "AlloySliderMenu"] 30 | path = AlloySliderMenu 31 | url = git://github.com/danielsefton/AlloySliderMenu.git 32 | [submodule "ti.sandtonio"] 33 | path = ti.sandtonio 34 | url = git://github.com/sandtonio/Alloy-Widgets.git 35 | [submodule "widget_info"] 36 | path = widget_info 37 | url = git://github.com/orthlieb/widget_info.git 38 | [submodule "scrollablewebview"] 39 | path = scrollablewebview 40 | url = git://github.com/orthlieb/widget_scrollablewebview.git 41 | [submodule "navigation"] 42 | path = navigation 43 | url = git://github.com/orthlieb/widget_navigation.git 44 | [submodule "combobox"] 45 | path = combobox 46 | url = git://github.com/orthlieb/widget_combobox.git 47 | [submodule "ti.pedro"] 48 | path = ti.pedro 49 | url = git://github.com/pec1985/Alloy-Widgets.git 50 | [submodule "nl.fokkezb.cachedImageView"] 51 | path = nl.fokkezb.cachedImageView 52 | url = git://github.com/FokkeZB/nl.fokkezb.cachedImageView.git 53 | [submodule "nl.fokkezb.dynamicScrolling"] 54 | path = nl.fokkezb.dynamicScrolling 55 | url = git://github.com/FokkeZB/nl.fokkezb.dynamicScrolling.git 56 | [submodule "nl.fokkezb.pullToRefresh"] 57 | path = nl.fokkezb.pullToRefresh 58 | url = git://github.com/FokkeZB/nl.fokkezb.pullToRefresh.git 59 | [submodule "nl.fokkezb.button"] 60 | path = nl.fokkezb.button 61 | url = https://github.com/FokkeZB/nl.fokkezb.button.git 62 | [submodule "nl.fokkezb.loading"] 63 | path = nl.fokkezb.loading 64 | url = https://fokkezb@github.com/FokkeZB/nl.fokkezb.loading.git 65 | [submodule "ytPlayer"] 66 | path = ytPlayer 67 | url = https://github.com/bob-sims/ytPlayer 68 | [submodule "raulriera_tableview"] 69 | path = raulriera_tableview 70 | url = https://github.com/raulriera/alloy-widgets 71 | -------------------------------------------------------------------------------- /MessageCenter/README.md: -------------------------------------------------------------------------------- 1 | Message Center 2 | ============ 3 | The message center widget is a fire and forget window that tracks any type of messages, whether that is from push notifications, messages from friends, system messages, etc. 4 | 5 | Example of the widget opening while simulating a push notification receipt. Also demonstrates the text, url, and map types: 6 | 7 | Installation 8 | ------------ 9 | For importing a widget in to your project see this link: 10 | 11 | Setup & Usage 12 | ------------ 13 | Currently there is no initialization parameters needed for the message center. Simply require the widget and fire off the methods you need: 14 | 15 | `var messageCenter = Alloy.createWidget("MessageCenter");` 16 | 17 | #### Open / Close the Message Center 18 | messageCenter.open(); 19 | messageCenter.close(); 20 | 21 | #### Create / Save a Message at anytime 22 | (Message Center does not have to be open to save a message) 23 | 24 | // Creates a message that points to a url 25 | messageCenter.createMessage({ 26 | title: "Christmas Special!", 27 | type: "url", 28 | content: "http://1.s3.envato.com/files/1143793/christmas_ad_preview3.jpg" 29 | }); 30 | 31 | The `type` specifies the type of message you are logging. This is used for loading the type of detail screen. e.g. "url" opens the content in a webView, "text" opens the content in just a label, "map" opens the content in a MapView. 32 | 33 | #### Create your own type 34 | If you want to create your own message type you'll need to create a new /type/detail.*.js controller. The widget controller will look at the "type" and automatically try to open that detail controller. 35 | 36 | #### Use with push notifications (iOS example) 37 | var messageCenter = Alloy.createWidget("MessageCenter"); 38 | 39 | Ti.Network.registerForPushNotifications({ 40 | types:[ 41 | Ti.Network.NOTIFICATION_TYPE_BADGE, 42 | Ti.Network.NOTIFICATION_TYPE_ALERT, 43 | Ti.Network.NOTIFICATION_TYPE_SOUND 44 | ], 45 | success: function(e) { 46 | 47 | }, 48 | error: function(e) { 49 | 50 | }, 51 | callback: function(e) { 52 | var data = e.data; 53 | var type = (data.type) ? data.type : "text"; 54 | var content = (data.content) ? data.content : ""; 55 | 56 | messageCenter.createMessage({ 57 | title: data.alert, 58 | type: type, 59 | content: content 60 | }); 61 | 62 | // Automatically open the detail window with this specific notification 63 | var detail = Alloy.createWidget("MessageCenter", "type/detail." + type, { 64 | title: data.alert, 65 | content: content, 66 | modal: true 67 | }); 68 | detail.window.open({ modal: true }); 69 | } 70 | }); -------------------------------------------------------------------------------- /MessageCenter/controllers/type/detail.map.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The parameters for this instance 3 | * @type {Object} 4 | * @example 5 | * { 6 | * title: {String} 7 | * content: {Object} e.g. { latitude: , longitude: } 8 | * modal: {Boolean} 9 | * } 10 | */ 11 | $.params = arguments[0]; 12 | 13 | // Set the map position 14 | $.map.region = { 15 | latitude: $.params.content.latitude, 16 | longitude: $.params.content.longitude, 17 | latitudeDelta: 0.1, 18 | longitudeDelta: 0.1, 19 | animate: true 20 | }; 21 | 22 | // Set the mapView annotation 23 | var annotation = Ti.Map.createAnnotation({ 24 | latitude: $.params.content.latitude, 25 | longitude: $.params.content.longitude, 26 | title: "Marker", 27 | subtitle: $.params.title, 28 | animate: true, 29 | rightButton: Ti.UI.iPhone.SystemButton.INFO_LIGHT 30 | }); 31 | $.map.addAnnotation( annotation ); 32 | 33 | // Bind the map click 34 | $.map.addEventListener('click', function(_event) { 35 | if(_event.clicksource === "rightButton") { 36 | Ti.UI.createAlertDialog({ 37 | title: $.params.content.latitude + " " + $.params.content.longitude, 38 | message: $.params.title 39 | }).show(); 40 | } 41 | }); 42 | 43 | // For modal versions of this screen 44 | if($.params.modal) { 45 | if(OS_IOS) { 46 | $.window.title = $.params.title || "Notification"; 47 | $.window.rightNavButton = (function() { 48 | var button = Ti.UI.createButton({ 49 | title: "Close" 50 | }); 51 | button.addEventListener("click", function() { 52 | $.window.close(); 53 | }); 54 | return button; 55 | })(); 56 | } 57 | } -------------------------------------------------------------------------------- /MessageCenter/controllers/type/detail.text.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The parameters for this instance 3 | * @type {Object} 4 | * @example 5 | * { 6 | * title: {String} 7 | * content: {String} 8 | * modal: {Boolean} 9 | * } 10 | */ 11 | $.params = arguments[0]; 12 | 13 | if($.params.title) { 14 | $.title.text = $.params.title; 15 | } 16 | 17 | if($.params.content) { 18 | $.content.text = $.params.content; 19 | } 20 | 21 | // For modal versions of this screen 22 | if($.params.modal) { 23 | if(OS_IOS) { 24 | $.window.title = $.params.title || "Notification"; 25 | $.window.rightNavButton = (function() { 26 | var button = Ti.UI.createButton({ 27 | title: "Close" 28 | }); 29 | button.addEventListener("click", function() { 30 | $.window.close(); 31 | }); 32 | return button; 33 | })(); 34 | } 35 | } -------------------------------------------------------------------------------- /MessageCenter/controllers/type/detail.url.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The parameters for this instance 3 | * @type {Object} 4 | * @example 5 | * { 6 | * title: {String} 7 | * content: {String} 8 | * modal: {Boolean} 9 | * } 10 | */ 11 | $.params = arguments[0]; 12 | 13 | if(!Ti.Network.online) { 14 | $.wrapper.add( 15 | Ti.UI.createLabel({ 16 | text: "You must be online to view this message", 17 | top: 10, 18 | right: 10, 19 | bottom: 10, 20 | left: 10, 21 | textAlign: "center" 22 | }) 23 | ); 24 | } else { 25 | if($.params.content) { 26 | $.content.url = $.params.content; 27 | } 28 | } 29 | 30 | // For modal versions of this screen 31 | if($.params.modal) { 32 | if(OS_IOS) { 33 | $.window.title = $.params.title || "Notification"; 34 | $.window.rightNavButton = (function() { 35 | var button = Ti.UI.createButton({ 36 | title: "Close" 37 | }); 38 | button.addEventListener("click", function() { 39 | $.window.close(); 40 | }); 41 | return button; 42 | })(); 43 | } 44 | } -------------------------------------------------------------------------------- /MessageCenter/controllers/widget.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Message Center Widget 3 | * 4 | * @version 1.0 5 | * @description A message center for managing any type of notifications 6 | * such as receiving push notifications, caching their content for later use, etc. 7 | **/ 8 | 9 | /** 10 | * The parameters for this instance 11 | * @type {Object} 12 | * 13 | * TODO Need configuration settings for type of window, table styling, 14 | * if there are multiple screen types || tabbed bar types 15 | * (e.g. notifications, friend requests, etc.) 16 | * 17 | * NOTE: Currently not used. 18 | */ 19 | $.params = arguments[0]; 20 | /** 21 | * Create a message in the notification center 22 | * @param {object} _data 23 | * @example 24 | * { 25 | * title: {String} The title of the message 26 | * type: {String} Message types determine the detail screen layout. 27 | * content: {String} The content of the message (varies depending on the type) 28 | * } 29 | * 30 | * TYPES - You can add your own types by placing a widget controller in the /type/ 31 | * folder. 32 | * 33 | * Currently supported types: 34 | * url - webView pointing to the url 35 | * text - simple text label display 36 | * map - MapView 37 | */ 38 | $.createMessage = function(_data) { 39 | // Get the message list 40 | var messages = Ti.App.Properties.getList("MESSAGE_CENTER_WIDGET_DATA") || []; 41 | 42 | // Add the new data and save to a new array 43 | messages.push( _data ); 44 | Ti.App.Properties.setList("MESSAGE_CENTER_WIDGET_DATA", messages); 45 | }; 46 | /** 47 | * Retrieve all messages 48 | * @return {Array} 49 | */ 50 | $.getMessages = function() { 51 | return Ti.App.Properties.getList("MESSAGE_CENTER_WIDGET_DATA") || []; 52 | }; 53 | /** 54 | * Get a particular message by it's index 55 | * @param {Number} _index 56 | * @return {Object} 57 | */ 58 | $.getMessage = function(_index) { 59 | var messages = Ti.App.Properties.getList("MESSAGE_CENTER_WIDGET_DATA"); 60 | return messages[_index]; 61 | }; 62 | /** 63 | * Remove a message by it's index 64 | * @param {Number} _index 65 | */ 66 | $.removeMessage = function(_index) { 67 | var messages = $.getMessages(); 68 | messages.splice(_index, 1); 69 | Ti.App.Properties.setList("MESSAGE_CENTER_WIDGET_DATA", messages); 70 | }; 71 | /** 72 | * Opens the message center 73 | */ 74 | $.open = function() { 75 | var data = $.getMessages(); 76 | var rows = []; 77 | 78 | // Loop through the existing messages and get the right row template 79 | if(data.length > 0) { 80 | data.forEach(function(_message) { 81 | var row = Ti.UI.createTableViewRow({ 82 | title: _message.title || _message.content, 83 | type: _message.type, 84 | font: { fontSize: 14 }, 85 | color: "#222", 86 | hasChild: true, 87 | height: 50 88 | }); 89 | rows.push( row ); 90 | }); 91 | } else { 92 | var row = Ti.UI.createTableViewRow({ 93 | title: "You have no messages", 94 | font: { fontSize: 14 }, 95 | color: "#222", 96 | height: 50 97 | }); 98 | rows.push( row ); 99 | } 100 | 101 | $.messageList.setData( rows ); 102 | 103 | $.window.open(); 104 | }; 105 | /** 106 | * Shortcut to close 107 | * NOTE: This is here in case additional closing logic is needed 108 | * or cleanup, etc. 109 | */ 110 | $.close = function() { 111 | $.window.close(); 112 | }; 113 | /** 114 | * Handle what happens when the user clicks on a row 115 | * @param {Object} _event 116 | */ 117 | $.handleRowClick = function(_event) { 118 | var index = _event.index; 119 | var type = _event.row.type; 120 | if(index !== null && type) { 121 | var detail = Alloy.createWidget("MessageCenter", "type/detail." + type, $.getMessage(index)); 122 | 123 | if(OS_IOS) { 124 | $.navgroup.open( detail.window ); 125 | } else { 126 | detail.window.open(); 127 | } 128 | } 129 | }; 130 | 131 | // Bind events 132 | if(OS_IOS) { 133 | $.closeButton.addEventListener("click", $.close); 134 | } else { 135 | $.messageList.addEventListener("longclick", function(_event) { 136 | var dialog = Ti.UI.createOptionDialog({ 137 | title: "Are you sure you want to delete this?", 138 | options: ["Delete", "Cancel"], 139 | cancel: 1 140 | }); 141 | dialog.show(); 142 | 143 | dialog.addEventListener('click', function (e) { 144 | if(e.index === 0) { 145 | $.removeMessage(_event.index); 146 | $.messageList.deleteRow(_event.index); 147 | } 148 | }); 149 | }); 150 | } 151 | $.messageList.addEventListener("click", $.handleRowClick); 152 | $.messageList.addEventListener("delete", function(_event) { 153 | $.removeMessage(_event.index); 154 | }); -------------------------------------------------------------------------------- /MessageCenter/styles/type/detail.text.tss: -------------------------------------------------------------------------------- 1 | "#wrapper": { 2 | layout: "vertical" 3 | }, 4 | "Label": { 5 | right: 10, 6 | left: 10, 7 | top: 10, 8 | bottom: 10, 9 | height: Ti.UI.SIZE 10 | }, 11 | "#title": { 12 | font: { fontSize: 18 } 13 | }, 14 | "#content": { 15 | font: { fontSize: 14 } 16 | } -------------------------------------------------------------------------------- /MessageCenter/styles/widget.tss: -------------------------------------------------------------------------------- 1 | "#window": { 2 | modal: true, 3 | backgroundColor: "#fff", 4 | navBarHidden: true 5 | }, 6 | "TableView": { 7 | editable: true 8 | } -------------------------------------------------------------------------------- /MessageCenter/views/type/detail.map.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /MessageCenter/views/type/detail.text.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | -------------------------------------------------------------------------------- /MessageCenter/views/type/detail.url.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /MessageCenter/views/widget.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /MessageCenter/widget.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "MessageCenter", 3 | "name": "MessageCenter", 4 | "description" : "", 5 | "author": "", 6 | "version": "1.0", 7 | "copyright":"Copyright (c) 2012", 8 | "license":"Public Domain", 9 | "min-alloy-version": "1.0", 10 | "min-titanium-version":"2.1", 11 | "tags":"", 12 | "platforms":"android,ios,mobileweb" 13 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | AlloyWidgets 2 | ============ 3 | Add alloy widgets here. 4 | -------------------------------------------------------------------------------- /starrating/README.md: -------------------------------------------------------------------------------- 1 | Star Rating 2 | ============ 3 | A simple star-rating widget. 4 | 5 | 6 | 7 | Installation & Usage 8 | ------------ 9 | 10 | 1. Download and unzip the widget code. 11 | 2. Copy to your app's `app` directory -- make sure the name of the folder is `starrating` 12 | 3. In your app's config.json file, edit the dependencies section as shown: 13 | 14 | "dependencies": { 15 | "starrating": "1.0" 16 | } 17 | 18 | 4. Add the widget tag to your view's XML as shown below. Set `max` to the number of stars you want shown, initialRating is optional and sets the starting value. 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 5. In your controller 27 | 28 | $.starwidget.init(); 29 | 30 | For more info about importing a widget in to your project see this link: 31 | 32 | #### Initialization parameters 33 | 34 | Set in XML or TSS: 35 | 36 | max = 5; // total number of stars 37 | initialRating = 0; // initial value selected 38 | 39 | #### Methods 40 | // set the current rating: 41 | $.starwidget.setRating(4); 42 | 43 | // get the current rating: 44 | var num = $.starwidget.getRating(); 45 | 46 | #### Callback 47 | 48 | (Optionally) Pass a call back function on initialization to be called each time the user taps a star 49 | 50 | function doFoo(num){ 51 | alert('Your rating = ' + num); 52 | } 53 | $.starwidget.init(doFoo); 54 | 55 | -------------------------------------------------------------------------------- /starrating/assets/star.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AppceleratorSolutions/AlloyWidgets/8825707d105df52a95c84a405729dd75e12fa26a/starrating/assets/star.png -------------------------------------------------------------------------------- /starrating/assets/star_half.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AppceleratorSolutions/AlloyWidgets/8825707d105df52a95c84a405729dd75e12fa26a/starrating/assets/star_half.png -------------------------------------------------------------------------------- /starrating/assets/star_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AppceleratorSolutions/AlloyWidgets/8825707d105df52a95c84a405729dd75e12fa26a/starrating/assets/star_off.png -------------------------------------------------------------------------------- /starrating/controllers/star.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AppceleratorSolutions/AlloyWidgets/8825707d105df52a95c84a405729dd75e12fa26a/starrating/controllers/star.js -------------------------------------------------------------------------------- /starrating/controllers/widget.js: -------------------------------------------------------------------------------- 1 | var args = arguments[0] || {}, 2 | stars = [], 3 | rating = 0, 4 | max = 5; 5 | 6 | // public method to set the rating 7 | var setRating = function(newRating) { 8 | // save newRating to the instance's rating property 9 | if(newRating > max) { 10 | newRating = max; 11 | } 12 | rating = newRating; 13 | 14 | // use a loop to set the stars[i].image property 15 | // using the half-image for fractional values 16 | for (var i = 0, l = stars.length; i < l; i++) { 17 | if (i >= rating) { 18 | stars[i].image = WPATH('star_off.png'); 19 | } 20 | else if (rating > i && rating < i+1) { 21 | stars[i].image = WPATH('star_half.png'); 22 | } 23 | else { 24 | stars[i].image = WPATH('star.png'); 25 | } 26 | } 27 | }; 28 | exports.setRating = setRating; 29 | 30 | // public method to get current rating 31 | exports.getRating = function() { 32 | return rating; 33 | }; 34 | 35 | // private method 36 | var createStars = function(num, cb) { 37 | for (var i = 0; i < num; i++) { 38 | // define the image view 39 | var star = Alloy.createWidget('starrating', 'star').getView(); 40 | 41 | if (!args.editable){ 42 | // use a closure (self-calling function) to add 43 | // a click-event listener that calls setRating 44 | // passing the value of i+1 45 | (function() { 46 | var index = i; 47 | star.addEventListener('click', function() { 48 | setRating(index+1); 49 | cb(index+1); 50 | }); 51 | })(); 52 | } 53 | // add the star image to the stars array 54 | stars.push(star); 55 | // add the star image to the instance view 56 | $.starrating.add(star); 57 | } 58 | }; 59 | 60 | exports.init = function (callback) { 61 | var max = args.max || 5, 62 | initialRating = args.initialRating || 0, 63 | cb = callback || function(){}; 64 | createStars(max, cb); 65 | setRating(initialRating); 66 | // can't apply view properties from the calling context when you use this widget 67 | // without coding them in to this controller, alloy limitation as of this writing 68 | _.extend($.starrating, args); 69 | 70 | }; 71 | -------------------------------------------------------------------------------- /starrating/starrating.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AppceleratorSolutions/AlloyWidgets/8825707d105df52a95c84a405729dd75e12fa26a/starrating/starrating.png -------------------------------------------------------------------------------- /starrating/styles/star.tss: -------------------------------------------------------------------------------- 1 | '.star': { 2 | height:'24dp', 3 | width:'24dp', 4 | left:'5dp' 5 | } 6 | -------------------------------------------------------------------------------- /starrating/styles/widget.tss: -------------------------------------------------------------------------------- 1 | "#starrating": { 2 | layout: 'horizontal', 3 | width: Ti.UI.SIZE, 4 | backgroundColor: 'transparent' 5 | } 6 | -------------------------------------------------------------------------------- /starrating/views/star.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /starrating/views/widget.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /starrating/widget.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "starrating", 3 | "name": "starrating", 4 | "description" : "A star-rating widget", 5 | "author": "", 6 | "version": "1.0", 7 | "copyright":"Copyright (c) 2012", 8 | "license":"Public Domain", 9 | "min-alloy-version": "1.0", 10 | "min-titanium-version":"3.0", 11 | "tags":"", 12 | "platforms":"android,ios,mobileweb" 13 | } --------------------------------------------------------------------------------