Jump to content

Chris.Smith

School User
  • Posts

    139
  • Joined

  • Last visited

Everything posted by Chris.Smith

  1. @sclough, Has this fixed your issue? I have checked your widget out on the beta build and I can't see any errors!? ~ Chris
  2. Michael, Assuming that it the colours are set (i.e. Bowman = red, Fulford = green etc) these could be set in styles as classes: td.bowman { background-color: red; } td.fulford { background-color: green; } or data attributes: td[data-house="bowman"] { background-color: 'red'; } td[data-house="fulford"] { background-color: 'green'; } Remember of course to set those attributes against the row when preparing the template either using $row.find('td[data-field=house]').addClass(datum.house) or using $row.find('td[data-field=house']).attr('data-house', datum.house). If this isn't the case you could dynamically generate the colours using Hexadecimal and cache the results to ensure the consistency. var house_colors = {}, getColorForHouse; getColorForHouse = function(house_name) { var data = {}; if (house_name in house_colors) { return house_colors[house_name]; } data.color = (Math.floor(Math.random() * 16777215)).toString(16); data.text = (parseInt(data.color, 16) > 0xffffff / 2) ? '000000' : 'FFFFFF'; house_colors[house_name] = data; return data; }; renderData = function(data) { data.forEach(function(datum) { var $row = $template.clone(), color_data = getColorForHouse(datum.house); for (var key in datum) { $row.find('[data-field='+key+']').text(datum[key]); } houses[datum.house] = null; tutor[datum.tutor] = null; applyTableFilter(); $row.find('[data-field=house]') .css({ 'backgroundColor': '#' + color_data.color, 'color': '#' + color_data.text }); $tbody.append($row); }.bind(this)); }.bind(this); Let me know how you get on ~ Chris
  3. Michael, Would you want the house cells to be specific colours? i.e. Bowman is always red because that is the house's colour ~ Chris
  4. @mobrien, No problems, let me know if you need any of it explaining / if you have any questions. ~ Chris
  5. Hi Paul, Remember when I said you probably won't find a need for .bind(this)?! Another alternative would be to "hoist" a variable in. var $mywrapper = this.element.find(".mywrapper"); $mywrapper.find('#wk1btn').on('click', function(ev) { $mywrapper.find('#week1').hide(); $mywrapper.find('#week2').show(); }); // ############################ OR ####################### var wklbtn = this.element.find(".mywrapper #wk1btn"); $wk1btn.click(function() { var $week1 = this.element.find('.mywrapper #week1'), $week2 = this.element.find('.mywrapper #week2'); $week1.hide(); $week2.show(); }.bind(this)); Hope this helps, ~ Chris
  6. Well Adrian, I think that is a brilliant idea! If only I knew an ICT teacher and VLE coordinator whose Year 11 and 13s where about to leave school thus freeing up some of his famously busy schedule allowing him to make said widget Honest answer is we are busy getting some surprises ready in time for conference; so we don't have a great deal of time. @pconkie are you up for a challenge?
  7. Hi @mobrien, Here is the code transformed to allow filtering. The styling you can add yourself <style> .template { display: none; } </style> <table class="table table-striped"> <thead> <th>Name</th> <th>House</th> <th>Tutor</th> <th>Achievement</th> <th>Behaviour</th> <th>Total</th> </thead> <tbody></tbody> </table> <table class="template"> <tbody> <tr> <td data-field="name"></td> <td data-field="house"></td> <td data-field="tutor"></td> <td data-field="achievement"></td> <td data-field="behaviour"></td> <td data-field="total"></td> </tr> </tbody> </table> <script type="text/javascript"> var returnSuccess = function(params, output) { return [ 200, "success", { "data": output, "status": "success", "params": params } ]; }, data = [ { name: 'Student 1', house: 'Bowman', tutor: 'ACR', achievement: 1100, behaviour: 10, total: 1090 }, { name: 'Student 2', house: 'Fulford', tutor: 'ACR', achievement: 1623, behaviour: 196, total: 1427 } ]; $.fixture('GET myapi.get', function(original, settings, headers) { return returnSuccess(original.data, data); }); $.fixture('GET myapi.sort', function(original, settings, headers) { var params = original.data, key = params.key, direction = params.direction, sorted_data = data.sort(function(a, b) { if (direction === 'asc') { if (a[key] < b[key]) { return -1; } else if (a[key] > b[key]) { return 1; } else { return 0; } } else { if (a[key] < b[key]) { return 1; } else if (a[key] > b[key]) { return -1; } else { return 0; } } }); return returnSuccess(params, sorted_data); }); $.fixture('GET myapi.filter', function(original, settings, headers) { var params = original.data, filtered_data = []; for (var key in params) { filtered_data = data.filter(function(datum) { if (datum[key].constructor.name !== "String") { return parseInt(params[key], 10) <= datum[key]; } return (new RegExp(params[key], 'gi')).test(datum[key]); }); } filtered_data = Array.prototype.concat.apply([], filtered_data); filtered_data = filtered_data.filter(function(datum, index, self) { return self.indexOf(datum) === index; }); return returnSuccess(params, filtered_data); }); </script> <script> var promise, renderData, applyTableFilter; if (steal.then !== undefined) { promise = steal('//frogui/components/mysorter/mysorter.js', '//frogui/components/tablefilter/tablefilter.js'); } else { promise = steal.import('frogui/components/mysorter/mysorter!frog-component', 'frogui/components/tablefilter/tablefilter!frog-component'); } promise.then(function() { var $table = this.element.find('table:first'), $tbody = $table.children('tbody'), $template = this.element.find('.template tbody > tr'), houses = {}, tutor = {}; $table.frogui_components_mysorter({ headers: { name: 'asc', house: 'asc', tutor: 'asc', achievement: 'asc', behaviour: 'asc', total: 'asc' } }); applyTableFilter = function() { $table.frogui_components_tablefilter({ filters: { name: { type: 'text', placeholder: 'Student Name', data: [], position: 0 }, house: { type: 'select', data: Object.keys(houses), placeholder: 'House', position: 1 }, tutor: { type: 'select', data: Object.keys(tutor), placeholder: 'Tutor', position: 2 }, achievement: { type: 'text', placeholder: 'Achievement Points', data: [], position: 3 }, behaviour: { type: 'text', placeholder: 'Behaviour Points', data: [], position: 4 }, total: { type: 'text', placeholder: 'Total', data: [], position: 5 } } }); $table.find('.filter-row, .filter-submit-row').removeClass('hide'); }; renderData = function(data) { data.forEach(function(datum) { var $row = $template.clone(); for (var key in datum) { $row.find('[data-field='+key+']').text(datum[key]); } houses[datum.house] = null; tutor[datum.tutor] = null; applyTableFilter(); $tbody.append($row); }.bind(this)); }.bind(this); $table.on('mysorter.sort', function(ev, sort) { Frog.Model.api('myapi.sort', sort) .done(function(resp) { $tbody.empty(); renderData(resp.data); }); }); $table.on('tablefilter.submit', function(ev, data) { Frog.Model.api('myapi.filter', data) .done(function(resp) { $tbody.empty(); renderData(resp.data); }); }); $table.on('tablefilter.reset', function(ev, data) { Frog.Model.api('myapi.get') .done(function(resp) { $tbody.empty(); renderData(resp.data); }); }); Frog.Model.api('myapi.get') .done(function(resp) { renderData(resp.data); }); }.bind(this)); </script> Feel free to tweak how it filters. It's just a rough first pass building on what I posted earlier. This has been a pleasant distraction from the project I'm currently on Any thoughts @Graham Quince?? ~ Chris
  8. Hi Paul, this.element does indeed refer to the HTML widget. this refers to the block scope bound to the executing function (code inside the script tag). element is a property of this. this.element.find(selector) is preferred over $(selector) because it is limited to performing a tree search over just the content within that particular HTML widget. This means you won't get any collisions should two instances of this widget be running at the same time. It is also more performant because it is only searching through a handful of DOM elements versus thousands. .bind(this) forces a function to operate within a specific block scope. For example: Class Person { constructor(name) { this.name = name; } getName() { return this.name; } } var paul = new Person("Paul"); // getName returns "Paul" assertEquals(paul.getName(), "Paul"); // true // getName with a custom scope returns "Chris" assertEquals(paul.getName.call({ name: 'Chris' }), "Paul"); // false assertEquals(paul.getName.call({ name: 'Chris' }), "Chris"); // true // Binding a function returns a clone of the function var bound_function = paul.getName.bind({ name: 'Chris' }); assertEquals(paul.getName(), "Paul"); // true assertEquals(bound_function(), "Chris"); // true // bound functions can be modified using .call() or .apply() assertEquals(bound_function.call(paul), "Chris"); // false assertEquals(bound_function.call(paul), "Paul"); // true To summarise, .bind() is a static version of .call() or .apply() however, it does not invoke the function, it merely returns a function that is bound to the provided scope. Here is a resource which can probably explain it better than I can - http://devdocs.io/javascript/global_objects/function/bind I hope you find this helpful. .bind(this) is fairly advanced stuff and unless you get into writing complex functional code, I think you probably won't use it that much. this.element is important though, and will help you avoid a number of bugs and errors that the community struggled with for a long time before it came along. Let me know if you have any more questions, ~ Chris
  9. Hi @mobrien, @pconkie's approach is a good start but there are a couple of things that you should both be aware of: Linking to external libraries in the HTML Widget works differently to how you expect, so the script tag loading in jquery.tablesorter.js from github won't work. Fortunately, FrogOS already has tablesorter under the hood. Using ID's is problematic, especially if you want to reuse this code in multiple places. Combine this.element and use either a tag lookup or add a class I recommend the using the tag. Using $(document).ready may produce random results as the document is already ready Instead, anything that needs to run at the end put in a script tag at the bottom of the code Personally, what I would try to do is reduce the amount of data I am storing in HTML by storing my data in JSON. This is a little scary at first but will make modification and maintenance easier. <style> .template { display: none; } </style> <table class="table table-striped"> <thead> <th>Name</th> <th>House</th> <th>Tutor</th> <th>Achievement</th> <th>Behaviour</th> <th>Total</th> </thead> <tbody></tbody> </table> <table class="template"> <tbody> <tr> <td data-field="name"></td> <td data-field="house"></td> <td data-field="tutor"></td> <td data-field="achievement"></td> <td data-field="behaviour"></td> <td data-field="total"></td> </tr> </tbody> </table> <script> var promise, renderData, data = [ { name: 'Student 1', house: 'Bowman', tutor: 'ACR', achievement: 1100, behaviour: 10, total: 1090 }, { name: 'Student 2', house: 'Fulford', tutor: 'ACR', achievement: 1623, behaviour: 196, total: 1427 } ]; if (steal.then !== undefined) { promise = steal('//frogui/components/mysorter/mysorter.js'); } else { promise = steal.import('frogui/components/mysorter/mysorter'); } promise.then(function() { var $table = this.element.find('table:first'), $tbody = $table.children('tbody'), $template = this.element.find('.template tbody > tr'); $table.frogui_components_mysorter({ headers: { name: 'asc', house: 'asc', tutor: 'asc', achievement: 'asc', behaviour: 'asc', total: 'asc' } }); renderData = function(data) { data.forEach(function(datum) { var $row = $template.clone(); for (var key in datum) { $row.find('[data-field='+key+']').text(datum[key]); } $tbody.append($row); }.bind(this)); }.bind(this); $table.on('mysorter.sort', function(ev, sort) { var key = sort.key, direction = sort.direction, sorted_data = data.sort(function(a, b) { if (direction === 'asc') { if (a[key] < b[key]) { return -1; } else if (a[key] > b[key]) { return 1; } else { return 0; } } else { if (a[key] < b[key]) { return 1; } else if (a[key] > b[key]) { return -1; } else { return 0; } } }); $tbody.empty(); renderData(sorted_data); }); renderData(data); }.bind(this)); </script> From there you could use the built $.fixture to move the data storage and sorting to an API based pattern. Thats a bit more advanced but would allow filtering of data! Let me know if that is of interest. ~ Chris
  10. @ADT, @gbligh, @vikpaw, @deanmoulder, I've left you all a present at http://www.frogcommunity.com/app/os?site=frogcode&page=showcase! Enjoy, ~ Chris
  11. Keep your eyes on the FrogCode - Showcase page on the Community as the download links will be going up on there in the next few days. ~ Chris
  12. Hi @pconkie, Sorry about that. It appears that your specified theme uuid is incorrect, odd that the information coming back is not entirely useful. Correct your theme uuid and it should work for you. You could also try a different method of programmatically creating sites. Unfortunately, we haven't written the documentation for this part of the system yet, so there will be an element of blind programming here. // We need the createsite component; if its not loaded, then get it // once loaded, run the function passed into .then() steal('//frogui/components/createsite/createsite.js').then(function() { var $el = $('<div />'); // Create a virtual element to "host" the component. $el.on('createsite.created', function(ev, site) { // A site is born! console.log(site.uuid); }); $el.on('createsite.error', function() { // error handling code goes here }); new Frogui.Controllers.Components.Createsite($el, { quiet: true, title: 'test45785d', launchOnCreate: false, theme: '7397AE892002C4FABE0FAFC9D06525054B7A007C5EV5C06D', description: 'my description', pages: [{ sequence: 0, name: 'Page 1', layout: 'F7B77DAF2002B37FD67E3F8F9EF9F50BFFB6EE2C088C91F6', parent_uuid: null, buckets: [] }] }); }); This approach is more complex but offers you can easy way of building the pages out. There is a lot of async code happening here, which will require a delicate hand to manage as I imagine you are gonna be looping through loads of data. If you would like more information on this approach let me know. Hope this helps ~ Chris
  13. Hi @pconkie, You are almost there! Frog.Model .api( 'sites.create', { page: [], quiet: true, name: 'test66', description: 'my description', theme: '7397AE892002C4FABE0FAFC9D06525054B7A007C5EV5C06D' }, { type: 'POST' } ).done(function(thisResponse) { var site = thisResponse.data.site; console.log(site.uuid); }); The third parameter is the options we want to send to jQuery to tell it how to send the request to the server! For more information, check out the api method's documentation here - https://froglearn.backwellschool.net/app/lib/packaging/docs/Com.Frog.Model.html Hope this helps, Chris
  14. Hi James, We are about to close down the Closed Beta Program as we transition into the Open Beta phase which would allow you to use FrogCode on your own FrogOS instance! I will flag this to @Graham Quince and we'll see about getting you some information. There is a FrogCode area on the community site (https://frog.frogcommunity.com/frogcode) which has videos and tutorials to prepare you for the event. Any questions, Let me know. ~ Chris
  15. Widget is fine... Though not without it's faults, it works just fine. This is a FrogOS issue not a FrogCode issue.
  16. Ok Folks, Public Service Announcement - @ADT found a bug in the Sites integration (in particular the Copy Page functionality). If a FrogCode widget is present on the page, the Copy Page function will break causing the sites application to lock up and hang. At this point the platform is only recoverable by refreshing/closing the tab. I have added a new page to the above site called "Known Bugs" which will have the status and release information for each bug that the community raises. In the meantime, avoid using FrogCode widgets with the Copy Pages functionality; If you need to copy a page with a FrogCode widget on, please temporarily remove the widget, copy the page and replace the widget and manually copy on to the new page.
  17. Think I can guess what numbers one and two will be
  18. Are you feeling alright @ADT? Is that... a compliment? Next you'll be coding!
  19. @Sue Busher as soon as you get Dickens you will be able to use the Package Manager to import and install widgets and apps found here on the Community! It won't mean that you will be able to tailor the widgets to your own needs but it will give you something in the meantime .
  20. You know @ADT, you could always show up @Graham Quince by writing the integration yourself in FrogCode . I'm sure the rest of the community would thank you endlessly..
  21. @ADT just checked our nightly builds and can confirm that it is in there. You will need to turn it on in Groups & Policies as it is off by default. There is even fine grained control of the functionality so different staff members can import packages from the community but only admins can install for example...
  22. @ADT Package Manager will/should be available for use as soon as you get Dickens (No waiting for FrogCode Editor) which means if you can convince someone to share their widgets/applications with you, you'll be able to import and install them
  23. Chris.Smith

    Egg Timer

    Who's rick? And why does he incur @ADT's wraith?
×
×
  • Create New...