• Jump To … +
    server.coffee src/actionknob.coffee src/autosem.coffee src/bitbucket_kba.coffee src/browserlog.coffee src/datareduction.coffee src/dci.coffee src/dciknob.coffee src/deeseeeye.coffee src/dnd.coffee src/doof.coffee src/formurla-mngr.coffee src/fractalpanel.coffee src/fractalpanel_test.coffee src/front.coffee src/ingestor.coffee src/kbabitbucket.coffee src/knobctrl.coffee src/lib_test.coffee src/nanoclock.coffee src/noodb.coffee src/noodbabstract.coffee src/noodbbrowser.coffee src/noodbbrowser_test.coffee src/noodbsec.coffee src/noorauth.coffee src/noorplugin.coffee src/noorquery.coffee src/noorvm.coffee src/noorwrite.coffee src/quadparser.coffee src/quadparsern3.coffee src/rbac.coffee src/reactor.coffee src/rebase.coffee src/rsrcidx.coffee src/sandboxactions.coffee src/screen_ctx.coffee src/spogi.coffee src/tabular_widget.coffee src/visctrl.coffee src/voicesknob.coffee src/whowhen.coffee src/xsd2native.coffee
  • tabular_widget.coffee

  • ¶
  • ¶

    Copyright 2012-2017, Nooron Collaboratory, Inc http://nooron.com

  • ¶

    These really

    ##

    if not document?
      document =
        getElementById: ->
        attachEvent: ->
    if not window?
      window =
        document: document
        navigator:
          userAgent: "Chrome" # TODO improve this
  • ¶

    ##

    VALUETYPE_2_XSD =
      boolean: "boolean"
      date: "date" # ISO8601
      datetime: "dateTime" # ISO8601
      duration: "duration"
      dollars: "decimal"
      five_stars: "int"
      html_snippet: "string" # TODO this should be escaped, how?  encoded as base64?
      integer: "int"
      letter_grade: "string"
      float: "float"
      plain: "string"
      timestamp: "timestamp" # TODO should this be 'timestamp'?
      username: "string"
      zero_to_one: "decimal"
      agent_types: "string"
    
    XSD_2_VALUETYPE = {}
    
    xsd_type_to_valuetype = (xsd_type) ->
      if not XSD_2_VALUETYPE[xsd_type]?
        for k,v of VALUETYPE_2_XSD
          if v is 'string'
            k = 'plain'
          XSD_2_VALUETYPE[v] = k
      return XSD_2_VALUETYPE[xsd_type]
    
    valuetype_to_xsd_type = (valuetype_id) ->
      return datatype_to_xsd_type[valuetype_id]
    
    localize_iso8601 = (date_str) ->
      date = new Date(date_str)
      minus = (d, i) -> new Date(d.getTime() - i)
      localize = (d) -> d.toISOString().substr(0, 19)
      return localize(minus(date, date.getTimezoneOffset()*60*1000))
    
    validate_iso8601 = ->
      throw_diff = (a, b) ->
        if a isnt b
          throw "#{a} isnt #{b}"
      throw_diff(localize_iso8601('2017-04-17T23:34:03Z'), '2017-04-17T17:34:03')
      throw_diff(localize_iso8601('2016-07-01T12:00:00Z'), '2016-07-01T06:00:00')
      throw_diff(localize_iso8601('2016-07-01T12:00:00Z'), '2016-07-01T06:00:00')
      return true
    
    
    widget_html = """
    <style type="text/css" class="ptwDynamicStyle"></style>
    <script type="text/javascript">console.log("WIDGET_HTML");Ponderate.jQuery = jQuery.noConflict(true);</script>
    
    <table class="ponderateTabularWidget" id="ptw__PNDR_ID">
      <caption class="contentEditableInEditMode"></caption>
      <thead>
        <tr class="ptwControls criterionRow">
          <td class="controls">
            <div class="windowShade">
              <span class="windowShadeHandle">══⇅════⇅══</span>
              <span class="windowShadeLabel"></span>
            </div>
            <div>
              <label class="authorizedUsersOnly editButton">
                 <input type="checkbox"/></label>
              <input
               class="editControl authorizedUsersOnly saveButton"
               type="button" value="Save">
            </div>
            <div class="controls">
              <div class="editControl authorizedUsersOnly addCriterionButton">
                  add Criterion</div>
              <div class="editControl authorizedUsersOnly addCandidateButton">
                  add Candidate</div>
            </div>
          </td>
        </tr>
      </thead>
      <tfoot>
        <tr>
          <td class="widenedByUnshadedCritCount" colspan="1">
            <span class="createdBy securityLabel"></span>
            <a style="display: none" class="poweredBy" href="http://nooron.com">Nooron</a>
          </td>
        </tr>
        <tr class="editControl">
          <td class="widenedByUnshadedCritCount" colspan="1" style="display:none">
            <label><span class="securityLabel">embed:</span>
                   <input class="htmlToEmbed"></input></label><br>
          </td>
        </tr>
        <tr class="editControl">
          <td class="widenedByUnshadedCritCount securityTd" colspan="1">
            <label><span class="securityLabel">readable by:</span>
                   <select class="readable_by"  name="readable_by"></select></label>
            <label><span class="securityLabel">writeable by:</span>
                   <select class="writeable_by" name="writeable_by"></select></label>
            <span class="shareButton">share</span>
          </td>
        </tr>
      </tfoot>
      <tbody class="disableSelect">
      </tbody>
    </table>
    """
    
    Ponderate = {}  if typeof Ponderate is "undefined"
    tabular_widget = (ptw_args) ->
      SUPPORTED_BROWSERS = "Google Chrome and Apple Safari"
  • ¶

    http://lawrence.ecorp.net/inet/samples/js-getelementsbyclassnameex.shtml

      gEBCN = (parentElement, class_name, tag) ->
  • ¶

    Extendeds function with performance improvements.

    * by using a parentElement and the tag the class is on
    * the list of elements to check is reduced.
    *
    * This function doesn't get bound to an object.
    
        docList = undefined
        matchArray = undefined
        re = undefined
        len = undefined
        tag = tag or "*"
        parentElement = parentElement or document
  • ¶

    object could be passed instead of Id

        if typeof(parentElement) is "string"
          parentElement = document.getElementById(parentElement)
  • ¶

    Get array of tags

        if tag is "*"
  • ¶

    The test is to accommodate IE 5,

       * leave it out if IE 5 support is not required
    
          docList = parentElement.all or parentElement.getElementsByTagName("*")
        else
          docList = parentElement.getElementsByTagName(tag)
  • ¶

    Create a regular expression object for class

        re = new RegExp("(?:^|\\s)" + class_name + "(?:\\s|$)")
  • ¶

    Create output array

        matchArray = new Array()
        len = docList.length - 1
  • ¶

    Populate output array from tag array

    * do loop is faster than for loop
    * albeit out is in reverse order
    
        while len
          matchArray[matchArray.length] = docList[len]  if re.test(docList[len].className)
          len--
  • ¶

    return matchArray.reverse(); //if the order needs to forward

        matchArray
  • ¶

    cstmGetElementsByClassNameExt2 if (typeof document.getElementsByClassName !== ‘function’){ document.getElementsByClassName = gEBCN; }

      sleep = (msec) ->
        start_time = new Date()
        a = 1  while new Date() - start_time < msec
    
      is_undefined = (v) ->
        no_val = undefined
        typeof(v) is typeof(no_val)
    
      is_defined = (v) ->
        no_val = undefined
        typeof(v) isnt typeof(no_val)
    
      get_event_target = (e) ->
  • ¶

    http://www.quirksmode.org/js/events_properties.html#target

        targ = undefined
        e = window.event  unless e
        if e.target
          targ = e.target
        else targ = e.srcElement  if e.srcElement
  • ¶

    defeat Safari bug

        targ = targ.parentNode  if targ.nodeType is 3
        targ
  • ¶

    http://msdn.microsoft.com/en-us/library/ms537509(v=vs.85).aspx console.log(navigator);

      is_IE = navigator.userAgent.indexOf("MSIE") > -1
      must_attachEvent = is_defined(document.attachEvent)
      display_none = "{display:none;}"
      copy_props_from_to_ = (pr, fr, to) ->
        for i of pr
          continue  unless pr.hasOwnProperty(i)
          k = pr[i]
          to[k] = fr[k]  if fr[k]
    
      is_boolean = (b) ->
        typeof(b) is typeof(true)
    
      has_value = (v) ->
        (typeof (v) isnt `undefined`) and (v isnt null) and (v isnt "")
    
      ascending_cmp = (a, b) ->
  • ¶

    if ((typeof(a) == typeof(true) && typeof(b) != typeof(true)) || (typeof(b) == typeof(true) && typeof(a) != typeof(true)) ) console.log(“type disparity:”,typeof(a),a,typeof(b),b);

        return 0  if a is b
        return 1  if a > b
        -1  if a < b
    
      descending_cmp = (a, b) ->
  • ¶

    if ((typeof(a) == typeof(true) && typeof(b) != typeof(true)) || (typeof(b) == typeof(true) && typeof(a) != typeof(true)) ) console.log(“type disparity:”,typeof(a),a,typeof(b),b);

        return 0  if a is b
        return -1  if a > b
        1  if a < b
    
      on_obj_put_proto = (obj, proto) ->
        proto.PROTO_WORKS = true
        obj.__proto__ = proto
        unless obj.PROTO_WORKS
          for k of proto
            obj[k] = proto[k]
        delete proto.PROTO_WORKS
    
      unicod =
        writing_hand: "&#x270d;"
        big_check: "&#x2714;"
        big_X: "&#x2716;"
        pencil_left: "&#x270e;"
        black_up_pointing_triangle: "&#x25b2;"
        black_down_pointing_triangle: "&#x25bc;"
    
      ptw_defaults =
        make_table_caption_draggable: false
        PROTO_WORKS: true
        table_id: "PNDR_ID"
        vert_bar: "&#x2503;<br>"
        basic_arrow: "&#x2503;<br>&#x2503;<br>&#x25BC;"
        svg_basic_arrow: "<img style=\"max-width:100%;max-height:100%\" src=\"http://upload.wikimedia.org/wikipedia/commons/f/ff/Arrow_south.svg\">"
        XXbasic_arrow: "<img src=\"http://alpha.ponderate.com/images/arrow.svg\" height=\"100%\"/>"
  • ¶

    basic_arrow: “
    “ + ““

        big_check: unicod.big_check
        big_X: unicod.big_X
        edit_icon: unicod.writing_hand
        down_arrow: unicod.black_down_pointing_triangle
        up_arrow: unicod.black_up_pointing_triangle
        being_edited: false
        interaction_mode: 'document' # vs 'spreadsheet'
        pndr8_title: "Title of Ponderation"
        _id: ""
        has_unsaved_changes: false
        show_edit_button: false
        base_url: "http://alpha.ponderate.com"
        stylesheet_link_tmpl: "<link title=\"SHEET_NAME\" rel=\"stylesheet\" href=\"BASE_URL/stylesheets/SHEET_NAME.css\"/>"
        readable_by: "workgroup"
        writeable_by: "workgroup"
        criteria_have_grab_grip_cell: false # to enable a feature being worked on
        display_candidate_link_edit_control: true
        display_candidate_edit_control: false # contentEditable instead
        display_candidate_delete_control: true
        display_criterion_default_order_control: true
        display_criterion_valuetype_control: true
        candidate_label_editable: true
        is_bookmarking_mode: false
        candidate__target: "target=\"_blank\" "
    
      if not ptw_args?
        ptw_args = {}
      on_obj_put_proto(ptw_args, ptw_defaults)
      ptw_vars =
        candidates_unsorted: true
        criteria_by_row_idx: []
        candidates_by_row_idx: []
        candidates_by_their_id: {}
        criteria_by_their_id: {}
        candidates_by_url: {}
        candidates_by_label: {}
        submitted_bugs: []
        num_crit_shaded: 0
  • ¶

    the vars (contains the args and the defaults)

      on_obj_put_proto(ptw_vars, ptw_args)
  • ¶

    /////////////////////////////////////////////// CANDIDATE

      candidate = (cand_spec) ->
        cand_spec = cand_spec or {}
        cand_spec.__proto__ =
          dirty: false
          unsorted: true
    
        cand_spec.data_by_crit_id = {}
        self =
          dirty: (b) ->
            if is_boolean(b)
              cand_spec.dirty = b
            cand_spec.dirty
    
          unsorted: (b) ->
            if is_boolean(b)
              cand_spec.unsorted = b
              pndr.candidates_unsorted(b)
            cand_spec.unsorted
    
          getElementsByClassName: (class_name, tag_hint) ->
            if cand_spec.tr.getElementsByClassName
              cand_spec.tr.getElementsByClassName class_name
            else
              gEBCN cand_spec.tr, class_name, tag_hint
    
          focus_on_datum_after: (prev_datum) ->
  • ¶

    FIXME find the next datum and ptw_vars. prev_datum.crit_id

          add_cand_to_table: (args) ->
            self.make_cand_table_elements()
            self.register_cand()
            self.add_column_for_each_criterion()
            self.update_cand_visible()
    
          id: (n) ->
            cand_spec.id
    
          tr: (tr) ->
            if tr
              cand_spec.tr = tr
              cand_spec.row_idx = tr.sectionRowIndex
            cand_spec.tr
    
          idx: (n) ->
            cand_spec.row_idx = n  if is_defined(n)
            cand_spec.row_idx
    
          url: (u) ->
            cand_spec.url = u  if is_defined(u)
            cand_spec.url
    
          delete_url: (args) ->
            delete cand_spec.url
    
          get_ponder_id_from_url: ->
            retval = self.url().split("/").reverse()[0]
  • ¶

    alert(retval);

            retval
    
          pack_cand_for_saving: (args) ->
            out =
              label: cand_spec.label
              id: cand_spec.id
    
            out.url = cand_spec.url  if cand_spec.url
            out
    
          data_for_saving: (args) ->
            out = []
            for crit_id of cand_spec.data_by_crit_id
              a_datum = cand_spec.data_by_crit_id[crit_id]
              if a_datum
                json = a_datum.pack_for_saving()
                out.push json  if json
            out
    
          make_cand_table_elements: (args) ->
            if is_undefined(cand_spec.row_idx)
              cand_spec.row_idx = ptw_vars.candidate_rows.childElementCount
  • ¶

    console.log(cand_spec,”inserting cand “

    + cand_spec.label + " at idx:"+cand_spec.row_idx);
    
            tr = ptw_vars.candidate_rows.insertRow(cand_spec.row_idx)
  • ¶

    if (! tr.getElementsByClassName){tr.prototype.getElementsByClassName = cstmGetElementsByClassNameExt2;}

            self.tr tr
            tr.setAttribute "class", "candidateRow"
            td_1 = tr.insertCell(0)
            td_1.setAttribute "class", "candidateCell"
            cand_label_span = document.createElement("span")
            td_1.appendChild cand_label_span
            cand_label_span.contentEditable = ptw_vars.candidate_label_editable
            cand_label_span.setAttribute "class", "candidateLabel contentEditableInEditMode"
            cand_label_span.onblur = pndr.onblur_candidate_label_handler # evt when focus leaves
            if ptw_vars.candidate_click_callback
              cand_label_span.onclick = ptw_vars.candidate_click_callback
            if ptw_vars.display_candidate_delete_control
              del_butt = document.createElement("span")
              del_butt.className = "candidateDeleteButton editControl"
              del_butt.innerHTML = unicod.big_X
              del_butt.onclick = pndr.del_candidate_handler
              td_1.appendChild del_butt
            if ptw_vars.display_candidate_link_edit_control
              link_edit_butt = document.createElement("span")
              link_edit_butt.className = "candidateLinkEditButton editControl"
  • ¶

    http://alpha.ponderate.com/pndr8/4f2a6860e3d62cfe0e00001e/hyperlink-icon-candidates

              link_edit_butt.innerHTML = "&#x21ac;" # BROKEN CIRCLE WITH NORTHWEST ARROW
              link_edit_butt.onclick = pndr.edit_candidate_link_handler
              td_1.appendChild link_edit_butt
            if ptw_vars.display_candidate_edit_control
              edit_butt = document.createElement("span")
              edit_butt.className = "candidateEditButton editControl"
              edit_butt.innerHTML = "&#x270d;" # WRITING HAND
              td_1.appendChild edit_butt
            if ptw_vars.candidate__drag_start_handler
    
              if false # TODO put a default on ptw_vars.make_candidate_drag_handles
                drag_handle = document.createElement("span")
                drag_handle.className = "candidateDragHandle fa fa-reorder"
                td_1.appendChild drag_handle
                $(drag_handle).attr("style","background-color:yellow")
              else
                drag_handle = tr
    
              $(drag_handle).draggable
                refreshPositions: true
                scroll: false
                snapTolerance: 500
                cursorAt:
                  top: 5
                  left: 5
                helper: (e, ui) ->
                  elem_to_drag = e.target
                  if elem_to_drag.tagName isnt 'TD'
                    elem_to_drag = $(e.target).parent("td")
  • ¶

    http://layout.jquery-dev.com/tips.cfm#Widget_Draggable

                  return $(elem_to_drag).clone().appendTo('body').
                    attr("class", "dnd_drag_helper").
                    css('zIndex', 5).
                    css('left', e.clientX). # position helper at mouse x location
                    css('top', e.clientY).  # and in the y dimension too
                    show()
    
                stop: ptw_vars.candidate__drop_handler
                start: ptw_vars.candidate__drag_start_handler
  • ¶

    ##

              $(drag_handle).draggable
                simple_helper: "clone"
                helper: (e, ui) ->
                  console.log e
  • ¶

    http://layout.jquery-dev.com/tips.cfm#Widget_Draggable

                  return $(this).clone().appendTo('body').css('zIndex', 5).show()
                handle: ".candidateDragHandle"
                stop: ptw_vars.candidate__drop_handler
                start: ptw_vars.candidate__drag_start_handler
  • ¶

    ## console.log ptw_vars.candidate__drag_start_handler

            td_1.setAttribute 'id', cand_spec.id
    
    
          remove: (args) ->
            candidate_section = ptw_vars.candidate_rows
            cand_spec.tr.parentNode.removeChild cand_spec.tr
            delete ptw_vars.candidates_by_row_idx[cand_spec.row_idx]
    
            delete ptw_vars.candidates_by_their_id[cand_spec.id]
    
            idx = cand_spec.row_idx
    
            while idx <= candidate_section.children.length
              other_cand = ptw_vars.candidates_by_row_idx[idx]
              if other_cand
                other_cand.register_cand()
              idx++
            self.delete_data_from_cand()
  • ¶

    TODO remove data update lower candidates so their row_idx is accurate re-register the remaining candidates in candidates_by_row_idx and candidates_by_their_id

          delete_data_from_cand: (args) ->
            for crit_id of cand_spec.data_by_crit_id
              a_datum = cand_spec.data_by_crit_id[crit_id]
              a_datum.crit_obj().forget_datum a_datum
    
          register_cand: (args) ->
            cand_spec.row_idx = cand_spec.tr.sectionRowIndex
            ptw_vars.candidates_by_row_idx[cand_spec.row_idx] = self
  • ¶
      ptw_vars.candidates_by_label[cand_spec.label]     = self;
    if (cand_spec.url){
        ptw_vars.candidates_by_url[cand_spec.url]     = self;
    }
    
  • ¶

    console.log(cand_spec.label,cand_spec.row_idx);

          update_cand_visible: (args) ->
            display_url = (cand_spec.url)
            if pndr.is_bookmarking_mode()
              a = document.createElement("a")
              a.appendChild document.createTextNode(cand_spec.label)
              a.onclick = pndr.choose_ponder_to_add_candidate_to_handler
  • ¶

    a.setAttribute(‘onclick’,”alert(evt)”);

              self.getElementsByClassName("candidateLabel")[0].appendChild a
            else if display_url
              url_on_ponderate = cand_spec.url[0] is "/"
              target = (if url_on_ponderate then "" else ptw_vars.candidate__target)
  • ¶

    FIXME remove all uses of ?EDIT var EDIT = (ptw_vars.show_edit_button && url_on_ponderate) ? “?EDIT”: ‘’; console.log(“EDIT=”+EDIT); +EDIT

              self.getElementsByClassName("candidateLabel")[0].innerHTML = "<a " + target + "href=\"" + cand_spec.url + "\">" + cand_spec.label + "</a>" #"("+cand_spec.row_idx+')</a>';
            else
              self.getElementsByClassName("candidateLabel")[0].innerHTML = cand_spec.label #+ "("+cand_spec.row_idx+")";
    
          add_column_for_each_criterion: (args) ->
            num_crit_shown = pndr.get_num_crit_unshaded()
            idx = 0
    
            while idx < ptw_vars.criteria_by_row_idx.length
  • ¶

    for (id in ptw_vars.criteria_by_their_id){

              obj = ptw_vars.criteria_by_row_idx[idx]
  • ¶

    var obj = ptw_vars.criteria_by_their_id[id]; console.log(‘add column:’,obj);

              self.add_column crit_obj: obj
              idx++
    
          add_column: (args) ->
            cell_index = cand_spec.tr.childElementCount - 1
            td = cand_spec.tr.insertCell(cell_index)
            css_classes = "eval"
            if args.crit_obj.cells_visualizable()
              css_classes += " ui-draggable"
              td.setAttribute "draggable", true
            td.setAttribute "class", css_classes #"eval"
            a_datum = undefined
            if pndr.being_edited()
              a_datum = @ensure_datum_for_criterion(args.crit_obj)
            else
              a_datum = @datum_for_crit_id(args.crit_obj.id())
            return  unless a_datum
            $(td).hide()  if cell_index >= pndr.get_num_crit_unshaded()
            a_datum.display_within td
            a_datum.update_datum_display()
    
          label: (n) ->
            cand_spec.label = n  if is_defined(n) and has_value(n)
            cand_spec.label
    
          add_datum_to_candidate: (d) ->
            self.datum_for_crit_id(d.crit_id(), d)
            self.discover_display_within_for_datum d
            d.update_datum_display()
    
          discover_display_within_for_datum: (d) ->
            d.display_within cand_spec.tr.children[d.crit_obj().idx()]  if d.crit_obj()  unless d.display_within()
    
          update_candidate_display: (args) ->
            idx = 0
    
            while idx < ptw_vars.criteria_by_row_idx.length
              crit_obj = ptw_vars.criteria_by_row_idx[idx]
              self.ensure_datum_for_criterion(crit_obj)
              crit_id = crit_obj.id()
              cand_spec.data_by_crit_id[crit_id].update_datum_display()
              idx++
    
          get_datum_by_crit_id: (crit_id) ->
            cand_spec.data_by_crit_id[crit_id]
    
          move_crit_datum_from_to_: (crit_obj, from_idx, to_idx) ->
            remove_idx = undefined
            if from_idx > to_idx
              insert_idx = to_idx
              remove_idx = from_idx + 1 # since it's shifted by insert
            if from_idx < to_idx
              remove_idx = from_idx
              insert_idx = to_idx + 1
            d_obj = cand_spec.data_by_crit_id[crit_obj.id()]
            new_cell = cand_spec.tr.insertCell(insert_idx)
            cand_spec.tr.removeChild cand_spec.tr.children[remove_idx]
            if d_obj
              d_obj.display_within new_cell
              d_obj.update_datum_display()
    
          ensure_datum_for_criterion: (crit_obj) ->
            d_obj = cand_spec.data_by_crit_id[crit_obj.id()]
            if d_obj
              return d_obj
            else
              d = crit_obj.spawn_datum({cand_id: self.id()})
              d = self.datum_for_crit_id(crit_obj.id(), d)
              self.discover_display_within_for_datum(d)
              return d
    
          datum_for_crit_id: (crit_id, d) ->
            if d
              cand_spec.data_by_crit_id[d.crit_id()] = d
              crit_obj = ptw_vars.criteria_by_their_id[d.crit_id()]
              d.crit_obj crit_obj
              d.cand_obj self
              d.update_datum_display()
            return cand_spec.data_by_crit_id[crit_id]
    
        self
  • ¶

    /////////////////////////////////////////////// DATUM

      datum = (datum_spec) ->
        self =
          value_is_saveable: (args) ->
  • ¶

    TODO review the logic in value_is_saveable

            datum_spec.value isnt `undefined` and datum_spec.value isnt ""
    
          pack_for_saving: (args) ->
            if self.value_is_saveable()
              return [datum_spec.cand_id, datum_spec.crit_id, self.get_saveable_value()]
    
          get_display_value: (args) ->
            self.crit_obj().crit_display_value(self)
    
          get_editable_value: (args) ->
            self.crit_obj().crit_editable_value(self)
    
          get_saveable_value: (args) ->
            self.crit_obj().crit_saveable_value(self)
    
          get_sortable_value: (args) ->
            self.crit_obj().crit_sortable_value(self)
    
          datum_coerce_to_valuetype: (args) ->
            self.crit_obj().crit_coerce_to_valuetype(self)
    
          crit_id: (n) ->
            datum_spec.crit_id
    
          crit_obj: (n) ->
            if n
              datum_spec.crit_obj = n
            datum_spec.crit_obj
    
          cand_obj: (n) ->
            if n
              datum_spec.cand_obj = n
            datum_spec.cand_obj
    
          write_datum_to_storage: (args) ->
            pndr.write_to_storage(self)
    
          set_value: (v) ->
  • ¶

    We set this here because valuetype.coerce_to_valuetype calls datum.value()

            initial_value = datum_spec.value
            datum_spec.value = v
            final_value = self.crit_obj().crit_coerce_to_valuetype(self)
            datum_spec.value = final_value
            if is_defined(initial_value) and (initial_value isnt final_value)
  • ¶

    FIXME push value to server

              unless self.write_datum_to_storage()
                self.cand_obj().dirty(true)
                self.cand_obj().unsorted(true)
            if self.get_valuetype().get_sort_onchange()
              pndr.sort_candidates_if_unsorted()
    
          value: -> # THIS IS THE GROUND OF THE VALUE CHAIN
            if datum_spec.reactor
              return datum_spec.reactor.get_value()
            return datum_spec.value
    
          input_element_id: ->
            datum_spec.crit_id + "__" + datum_spec.cand_id + "__" + "input"
    
          display_within: (n) ->
            datum_spec.display_within = n  if n
            datum_spec.display_within
    
          get_valuetype: ->
            self.crit_obj().valuetype()
    
          cell_being_edited: ->
            retval = not not (pndr.is_spreadsheet_mode() and pndr.datum_being_edited is @)
            return retval
    
          update_datum_display: (args) =>
            vt = self.get_valuetype()
            if self.display_within()
              edit_this_cell_only = self.cell_being_edited()
              if pndr.being_edited() or edit_this_cell_only
                self.display_within().innerHTML = self.get_editable_value()
                inp = document.getElementById(self.input_element_id())
                if inp
                  if edit_this_cell_only
                    inp.onblur = (evt) ->
                      console.log "onblur", evt
                    inp.onchange = pndr.make_cell_value_onchange_handler(self)
                    inp.focus()
                  else
                    inp.onchange = pndr.set_datum_value
                  inp.onblur = pndr.sort_candidates_if_unsorted
              else
                v = self.get_display_value()
                self.display_within().innerHTML = (if (v isnt `undefined`) then v else "")
                if pndr.is_spreadsheet_mode()
                  self.display_within().onclick = (evt) ->
                    self.spreadsheet_cell_onclick_handler(evt)
    
          spreadsheet_cell_onclick_handler: (evt) ->
            self.display_within().onclick = null # disable once it has been used
            prior = pndr.datum_being_edited
            pndr.datum_being_edited = @
            if prior
              prior.update_datum_display()
            self.update_datum_display()
    
        self
  • ¶

    /////////////////////////////////////////////// CRITERION Note the use of crit_spec as a closure within self.

    • self: is a facade with exposed methods
    • crit_spec: holds state, externally inaccessible A similar technique is used by datum and candidate. https://en.wikipedia.org/wiki/Facade_pattern

    TODO refactor criterion, datum and candidate TODO https://bitbucket.org/smurp/libnoo/issues/186/ https://github.com/dustinboston/coffeescript-design-patterns#facade https://github.com/dustinboston/coffeescript-design-patterns#flyweight https://github.com/dustinboston/coffeescript-design-patterns#proxy

    Problems:

    • each ‘instance’ has a complete copy of every function!!! Seriously

    Proposed Solution http://stackoverflow.com/a/4686345/1234699

  • ¶

    Criterion_defaults: # TODO put on Criterion class but protect from writing? smaller_values_better: false default_order: +1 cells_visualizable: false class Criterion constructor: (crit) -> # crit is equivalent to the existing ‘crit_spec’ if not crit crit = {} crit.proto = Criterion_defaults @id: -> crit.id

      criterion = (crit_spec) ->
        crit_spec = {}  unless crit_spec
        crit_defaults =
  • ¶

    sort_order: ‘DESC’, // vs ASC vs NULL

          smaller_values_better: false
          default_order: +1 # ie NO CHANGE FROM THE VALUETYPE DEFAULT ORDER
          cells_visualizable: false
    
        crit_spec.__proto__ = crit_defaults
        crit_spec.associated_data = []
        self =
          spawn_datum: (args) ->
            a_datum = datum(
              cand_id: args.cand_id # TODO save cand_obj: directly
              crit_id: self.id() # TODO save crit_obj: directly
              value: ""
            )
            if crit_spec.formula
              a_datum.reactor = self.interactor().spawn_reactor(a_datum, crit_spec.formula)
            return a_datum
    
          formula: (formula) ->
            if formula?
              crit_spec.formula = formula
            return crit_spec.formula
    
          interactor: (ntrctr) ->
            if not ntrctr
              crit_spec.interactor = ntrctr
            return crit_spec.interactor
    
          get_cmp: (args) ->
  • ¶
    if (crit_spec.valuetype.cmp_function){
        alert("using cmp_function");
        return crit_spec.valuetype.cmp_function;
    }
    
            if crit_spec.valuetype
              return crit_spec.valuetype.cmp
            descending_cmp
    
          default_order: (i) ->
            crit_spec.default_order = i  if is_defined(i)
            crit_spec.default_order
    
          cells_visualizable: (n) ->
            crit_spec.cells_visualizable = n if n? # assign new value if present
            crit_spec.cells_visualizable
    
          add_crit_to_table: (args) ->
            self.discover_valuetype()
            self.make_crit_table_elements args
            self.register_crit()
            self.update_crit_visible()
    
          valuetype_picker_id: (args) ->
            self.id() + "__valuetype_picker"
    
          valuetype_id: (n) ->
            crit_spec.valuetype_id = n  if n? # assign new value if present
            crit_spec.valuetype_id
    
          valuetype: (vt) ->
            crit_spec.valuetype = vt  if vt? # assign new value if present
            crit_spec.valuetype
    
          set_valuetype_by_xsd: (xsd_type) ->
            crit_spec.valuetype = xsd_type_to_valuetype(xsd_type)
            console.log crit_spec.valuetype
            crit_spec.valuetype
    
          draggable_thing: ->
            crit_spec.draggable_thing
    
          label: (label) ->
            crit_spec.label = label  if is_defined(label)
            crit_spec.label
    
          idx: (n) ->
            crit_spec.row_idx = n  if is_defined(n)
            crit_spec.row_idx
    
          getElementsByClassName: (class_name, tag_hint) ->
            if crit_spec.tr.getElementsByClassName
              crit_spec.tr.getElementsByClassName class_name
            else
              gEBCN crit_spec.tr, class_name, tag_hint
    
          tr: (tr) ->
            if tr
              crit_spec.tr = tr
              crit_spec.row_idx = tr.sectionRowIndex
              ptw_vars.criteria_by_row_idx[crit_spec.row_idx] = self
            crit_spec.tr
    
          id: (n) ->
            crit_spec.id = n  if n # FIXME reregister everything when ID changes
            crit_spec.id
    
          id_of_html_elem: ->
            "crit__" + self.id()
    
          discover_crit_label: (args) ->
            self.label self.tr().getElementsByClassName("criterionLabel")[0].innerHTML # TEST [0] is untested
    
          pack_crit_for_saving: (args) ->
            out =
              label: self.discover_crit_label()
              valuetype_id: self.valuetype_id()
              default_order: self.default_order()
              id: self.id()
            out
    
          discover_valuetype: (args) ->
            a_vt = valuetype_factory.get(crit_spec.valuetype_id)
            return  unless a_vt
  • ¶

    a_vt = valuetypes[‘plain’];

            crit_spec.valuetype = a_vt
    
          crit_display_value: (a_datum) ->
            return crit_spec.valuetype.get_display_value(a_datum)  if crit_spec.valuetype
            ""
    
          crit_saveable_value: (a_datum) ->
            return crit_spec.valuetype.get_saveable_value(a_datum)  if crit_spec.valuetype
            ""
    
          crit_sortable_value: (a_datum) ->
            return crit_spec.valuetype.get_sortable_value(a_datum)  if crit_spec.valuetype
            ""
    
          crit_editable_value: (a_datum) ->
            return crit_spec.valuetype.get_editable_value(a_datum)  if crit_spec.valuetype
            ""
    
          crit_coerce_to_valuetype: (a_datum) ->
            crit_spec.valuetype.vt_coerce_to_valuetype a_datum  if crit_spec.valuetype
    
          is_hidden: (args) ->
            $(self.crit_label_box()).is ":hidden"
    
          crit_label_box: ->
            r = crit_spec.tr.lastChild
            r
    
          crit_arrow_box: ->
  • ¶

    This is tricky! The arrow_box for a criterion is the td.arrow_box of the row below the one containing the crition label. That is, a criterion’s arrow_box is the arrow below the criterion name, which is neccessarily in the row (tr) below the criterion name. This works even for the bottom criterion because that row is followed by the row containing the windowShade, which also has a td.arrow_box as its first element.

            console.log "nextSibling of ", crit_spec.tr.rowIndex, crit_spec.tr, "is", crit_spec.tr.nextSibling and crit_spec.tr.nextSibling.rowIndex, crit_spec.tr.nextSibling  if window.DEBUG_SHADING
            td = crit_spec.tr.nextSibling.firstChild
            for child in crit_spec.tr.nextSibling.children
              if $(child).hasClass('arrow_box')
                return child
  • ¶

    $(td).attr(‘style’, “background:red”)

            console.log "a child with class .arrow_box was expected in",crit_spec.tr.nextSibling
            debugger
  • ¶

    console.log(“#TD in TR”,crit_spec.tr.children.length,crit_spec.tr.id);

          hide: (args) ->
            $(self.crit_label_box()).hide()
            arrow = self.crit_arrow_box()
            console.log "hiding arrow in row " + arrow.parentNode.rowIndex + " labelled: \"" + arrow.parentNode.innerText + "\""  if window.DEBUG_SHADING
            $(arrow).hide()
  • ¶

    if not $(arrow).is(‘:hidden’) debugger

  • ¶
    if (crit_spec.tr.rowIndex == 1){
        // hide the arrow_box beside the controls
        $(ptw_vars.wgt_controls.firstChild).hide();
        if (window.DEBUG_SHADING)
                       console.log("arrowbox beside controls hidden: ",$(ptw_vars.wgt_controls.firstChild).is(':hidden'));
    }
    
            self.hide_column()
    
          hide_column: (args) ->
            self.for_each_cell_in_column_do "hide"
    
          show: (args) ->
  • ¶

    console.log(self,”show()”);

            $(self.crit_label_box()).show()
            $(self.crit_arrow_box()).show()
            self.show_column()
    
          show_column: (args) ->
            self.for_each_cell_in_column_do "show"
    
          for_each_cell_in_column_do: (func_name) ->
            col_idx = crit_spec.row_idx
            idx = 0
    
            while idx < ptw_vars.candidates_by_row_idx.length
              a_cand_row = ptw_vars.candidate_rows.children[idx]
              $(a_cand_row.children[col_idx])[func_name]()  if a_cand_row
              idx++
    
          make_crit_table_elements: (args) ->
            args = {}  unless args
  • ¶

    FIXME when the sort controls get added this will need work do not insert at the end, that is where the controls are args.row_idx = ptw_vars.wgt_table.tHead.childElementCount - 1; // fails on IE

            args.row_idx = ptw_vars.criteria_by_row_idx.length  if args.row_idx is `undefined`
  • ¶

    var tr = ptw_vars.wgt_table.tHead.insertRow(args.row_idx);

            tr = ptw_vars.criterion_rows.insertRow(args.row_idx)
  • ¶

    if (! tr.getElementsByClassName){tr.getElementsByClassName = cstmGetElementsByClassNameExt2;}

            tr.id = self.id_of_html_elem()
            tr.setAttribute "class", "criterionRow" # " fa arrows-v"
            td_1 = tr.insertCell(0)
            elem_to_drag = td_1
            td_1.setAttribute "class", "criterionControl"
            if ptw_vars.have_special_grab_grip_cell
              td_2 = tr.insertCell(-1)
              elem_to_drag = td_2
  • ¶

    td_2.setAttribute “class”, “fa arrows-v” # Why does this not work?

            elem_to_drag.ondragend = pndr.drop_criterion
            crit_label_span = document.createElement("span")
            crit_label_span.contentEditable = true
            td_1.appendChild crit_label_span
            crit_label_span.setAttribute "class", "criterionLabel contentEditableInEditMode"
            crit_label_span.onblur = pndr.onblur_criterion_label_handler
            crit_label_span.onfocus = pndr.onfocus_criterion_label_handler
            if ptw_vars.display_criterion_default_order_control
              ord_butt = document.createElement("span")
              ord_butt.setAttribute "class", "criterionOrderButton"
  • ¶

    “criterionOrderButton editControl contentAlterableInEditMode”);

              $(ord_butt).click pndr.reverse_criterion_default_order_handler
              td_1.appendChild ord_butt
            if ptw_vars.display_criterion_valuetype_control
              crit_valuetypeid_span = document.createElement("span")
              td_1.appendChild crit_valuetypeid_span
              crit_valuetypeid_span.setAttribute "class", "valuetypeId contentAlterableInEditMode"
              crit_valuetypeid_span.onclick = (if ptw_vars.being_edited then pndr.display_criterion_valuetype_selector else null)
            crit_spec.draggable_thing = td_1
            crit_spec.draggable_thing.draggable = true # FIXME see set_visibility_of_controls
            crit_spec.tr = tr # so we can reach the tr from the crit_obj
    
          register_crit: (args) ->
            crit_spec.row_idx = crit_spec.tr.sectionRowIndex
            ptw_vars.criteria_by_row_idx[crit_spec.row_idx] = self
    
          data_will_be_lost_if_valuetype_changes_to: (evt) ->
            current_valuetype = self.valuetype()
            proposed_valuetype = pndr.get_valuetype_by_id(evt.target.value)
            console.log "data_will_be_lost_if_valuetype_changes_to:", current_valuetype, current_valuetype.id(), proposed_valuetype.id(), evt.target.value
            return false  if current_valuetype is proposed_valuetype
            not current_valuetype.can_be_converted_without_loss_to(proposed_valuetype)
    
          remember_datum: (a_datum) ->
            crit_spec.associated_data.push a_datum
    
          forget_datum: (a_datum) ->
            io = crit_spec.associated_data.indexOf(a_datum)
            crit_spec.associated_data.unshift io  if io > -1
    
          num_of_candidates_with_non_defaulted_data: (args) ->
            crit_spec.associated_data.length
    
          display_valuetype_selector: (args) ->
            self.getElementsByClassName("valuetypeId")[0].innerHTML = valuetype_picker(
              value: crit_spec.valuetype_id
              criterion: self
            )
            document.getElementById(self.valuetype_picker_id()).onchange = pndr.set_criterion_valuetype_handler
            document.getElementById(self.valuetype_picker_id()).onblur = pndr.set_criterion_valuetype_handler
    
          update_crit_visible: (args) ->
            self.getElementsByClassName("criterionLabel")[0].innerHTML = crit_spec.label
            if crit_spec.valuetype_id
              show_idx = (if false then " " + crit_spec.row_idx + " " else "")
              self.getElementsByClassName("valuetypeId")[0].innerHTML = crit_spec.valuetype_id  if ptw_vars.display_criterion_valuetype_control
              if ptw_vars.display_criterion_default_order_control
                if self.default_order() is 1
                  self.getElementsByClassName("criterionOrderButton")[0].innerHTML = ptw_vars.down_arrow
                else
                  self.getElementsByClassName("criterionOrderButton")[0].innerHTML = ptw_vars.up_arrow
            else
              self.display_valuetype_selector()
    
          ensure_arrow_cell_beside: (args) ->
            if crit_spec.tr.children.length is 1
              pndr.add_arrow_cell_to target: crit_spec.tr
            return
    
          increase_height_of_arrow: (args) ->
            crit_spec.tr.children[0].setAttribute "rowspan", pndr.get_num_criteria() - crit_spec.row_idx + 1
    
          push_down: (args) ->
            self.register_crit()
            self.ensure_arrow_cell_beside()
            self.increase_height_of_arrow()
            self.update_colspan()
            self.update_crit_visible()
    
          update_colspan: (args) ->
            lbl_cell = crit_spec.tr.children[1]
            if lbl_cell
              lbl_cell.setAttribute "colspan", pndr.get_num_columns() - crit_spec.row_idx
    
        self
  • ¶

    /////////////////////////////////////////////// VALUETYPE

      valuetype = (vtspec) ->
        vtspec = {}  unless vtspec
        on_obj_put_proto vtspec,
          can_be_converted_to_from_any_valuetype: false
          sort_onchange: true
  • ¶

    descending_cmp: descending_cmp, ascending_cmp: ascending_cmp,

          cmp: descending_cmp
    
        self =
          id: (id) ->
  • ¶

    write once, not changeable

            vtspec.id = id  if is_defined(id) and is_undefined(vtspec.id)
            vtspec.id
    
          cmp: (a_datum, b_datum) ->
            return vtspec.cmp(a_datum, b_datum)
    
          the_display_value: (a_datum) ->
            if vtspec.display_value_func
              return vtspec.display_value_func(a_datum.value())
            if vtspec.value_to_display
              return vtspec.value_to_display[a_datum.value()]
            return a_datum.value()
    
          get_sort_onchange: ->
            vtspec.sort_onchange
    
          can_be_converted_without_loss_to: (another_valuetype) ->
            vtspec.can_be_converted_to_from_any_valuetype
    
          get_select_input: (a_datum) ->
            k = undefined
            v = a_datum.value()
            iei = a_datum.input_element_id()
            out = "<select id=\"" + iei + "\" name=\"" + iei + "\">\n"
            out += "<option value=\"\"></option>\n"
            for k of vtspec.value_to_display
              sel = (if k is v + "" then " selected" else "")
              out += "<option" + sel + " value=\"" + k + "\">" + vtspec.value_to_display[k] + "</option>\n"
            out += "</select>\n"
            out
    
          get_editable_value: (a_datum) ->
            if vtspec.value_to_display
              return self.get_select_input(a_datum)
            v = self.the_display_value(a_datum)
            v = (if v then v else "")
            v = v.replace(/\"/, "\"").replace(/\</, "&lt;").replace(/\>/, "&gt;")  if vtspec.escape_to_edit
            if vtspec.html_input
              retval = "<input "
              retval += "name=\"" + a_datum.input_element_id() + "\" "
              retval += "id=\"" + a_datum.input_element_id() + "\" "
              for key of vtspec.html_input
                val = vtspec.html_input[key]
                val = v  if key is "value"
                if key is "value" and v is `undefined`
                  retval += "value=\"" + v + "\" "
                else
                  retval += key
                  retval += "=\"" + val + "\" "  if typeof val isnt "undefined"
              retval += "/>"
              retval
            else
              if vtspec.edit_in_textarea
                self.wrap_in_prefix_postfix "<textarea " + "name=\"" + a_datum.input_element_id() + "\"" + "id=\"" + a_datum.input_element_id() + "\"" + ">" + v + "</textarea>"
              else
                self.wrap_in_prefix_postfix "<input type=\"text\" " + "name=\"" + a_datum.input_element_id() + "\"" + "id=\"" + a_datum.input_element_id() + "\"" + " size=\"10\" value=\"" + v + "\"/>"
    
          wrap_in_prefix_postfix: (s) ->
            prefix = vtspec.prefix or ""
            postfix = vtspec.postfix or ""
            if s
              return prefix + ((if s then s else "")) + postfix
            ""
    
          wrap_in_aligner: (s) ->
            return "<div align=\"" + vtspec.align + "\">" + s + "</div>"  if vtspec.align
            s
    
          get_display_value: (args) ->
            self.wrap_in_aligner self.wrap_in_prefix_postfix(self.the_display_value(args))
    
          get_saveable_value: (a_datum) ->
            retval = undefined
            if vtspec.get_saveable_value
              retval = vtspec.get_saveable_value(a_datum)
            else
              retval = a_datum.value()
            if vtspec.get_saveable_value_regexp
              m = ("" + retval).match(vtspec.get_saveable_value_regexp)
  • ¶

    get first non-empty match

              i = 0
    
              while i < m.length
                if m[i]
                  retval = m[i]
                  break
                i++
  • ¶

    console.log(‘vtspec’,m,retval);

            retval = vtspec.caster(retval)  if vtspec.caster
            retval
    
          get_sortable_value: (a_datum) ->
            retval = undefined
            if vtspec.get_sortable_value
              retval = vtspec.get_sortable_value(a_datum)
            else
              retval = vtspec.get_saveable_value(a_datum)
            retval
    
          vt_coerce_to_valuetype: (a_datum) ->
            v = a_datum.value()
            if vtspec.value_to_saveable
              if vtspec.value_to_saveable[v] isnt `undefined`
                return vtspec.value_to_saveable[v]
            if vtspec.vt_coerce_to_valuetype
              return vtspec.vt_coerce_to_valuetype(a_datum)
            return a_datum.value()
    
        self
  • ¶

    see duplication in app.js

      choosable_agent_types =
        self: "self"
        workgroup: "workgroup"
        family: "family"
        friends: "friends"
        public: "public"
  • ¶

    http://www.mredkj.com/javascript/nfbasic.html

      add_commas = (nStr) ->
        nStr += ""
        x = nStr.split(".")
        x1 = x[0]
        x2 = (if x.length > 1 then "." + x[1] else "")
        rgx = /(\d+)(\d{3})/
        x1 = x1.replace(rgx, "$1" + "," + "$2")  while rgx.test(x1)
        x1 + x2
    
      remove_commas = (nStr) ->
        nStr = "" + nStr
        parseFloat (nStr).replace(/\,/g, "")
    
      remove_dollars = (nStr) ->
        nStr = "" + nStr
        parseFloat (nStr).replace(/\$/g, "")
    
      good_number = (num) ->
        (if isNaN(num) then `undefined` else num)
    
      primitive_valuetypes =
        agent_type: valuetype(
          value_to_display: choosable_agent_types
          display_to_value: choosable_agent_types
        )
        boolean: valuetype(
          value_to_display:
            true: unicod.big_check
            false: unicod.big_X
    
          value_to_saveable:
            true: true
            false: false
            0: true
            1: false
        )
        date: valuetype(
          html_input:
            type: "date"
          get_saveable_value: (a_datum) ->
            d = new Date(a_datum.value())
            d.toJSON().substr(0, 10)
        )
    
        datetime: valuetype(
          html_input:
            type: "datetime-local"
            value: undefined # ie get the value from the datum
          display_value_func: (a_datum) ->
  • ¶

    Convert a_datum from whatever TZ it is in to local time and remove all mention of TZ. eg 2016-07-01T12:00:00Z ==> 2016-07-01T06:00:00 (for MDT local)

            if a_datum is a_datum.substr(0,19)
              console.log("assuming #{a_datum} is Zulu time")
  • ¶

    TODO expose a warning mark in the UI that zulu time is assumed

            localize_iso8601(a_datum)
          get_saveable_value: (a_datum) ->
            console.log(a_datum, "is being saved incorrectly")
            d = new Date(a_datum + 'Z')
            d.toJSON()
        )
  • ¶

    return ‘Date(‘+d.UTC()+’)’;

        dollars: valuetype(
          prefix: "$"
          get_saveable_value_regexp: /(\d*\.?\d*)/g
          align: "right"
          display_value_func: add_commas
          vt_coerce_to_valuetype: (a_datum) ->
            good_number parseFloat(remove_dollars(remove_commas(a_datum.value())))
        )
        five_stars: valuetype(
          align: "left"
          value_to_display:
  • ¶

    0: ‘’,

            1: "&#x2605;"
            2: "&#x2605;&#x2605;"
            3: "&#x2605;&#x2605;&#x2605;"
            4: "&#x2605;&#x2605;&#x2605;&#x2605;"
            5: "&#x2605;&#x2605;&#x2605;&#x2605;&#x2605;"
    
          value_to_saveable:
            5: 5
            4: 4
            3: 3
            2: 2
            1: 1
            0: 0
            "": 0
        )
        html_snippet: valuetype
          escape_to_edit: true
          edit_in_textarea: true
    
        integer: valuetype
          align: "right"
          display_value_func: add_commas
          vt_coerce_to_valuetype: (a_datum) ->
            good_number parseInt(remove_commas(a_datum.value()), 10)
    
        letter_grade: valuetype(
          cmp: ascending_cmp
          value_to_display:
            A: "&#x24B6"
            B: "&#x24B7"
            C: "&#x24B8"
            D: "&#x24B9"
            E: "&#x24BA"
            F: "&#x24BB"
    
          references: ["http://www.iam.uni-bonn.de/~alt/html/unicode_170.html"]
        )
        float: valuetype
          align: "right"
          display_value_func: add_commas
          get_saveable_value: (a_datum) ->
            good_number parseFloat(a_datum.value())
          vt_coerce_to_valuetype: (a_datum) ->
            good_number parseFloat(a_datum.value())
    
        plain: valuetype
          can_be_converted_to_from_any_valuetype: true
    
        duration: valuetype() # http://www.datypic.com/sc/xsd/t-xsd_duration.html
    
        timestamp: valuetype
          get_saveable_value: (a_datum) ->
            d = new Date(a_datum.value())
            d.toJSON()
          display_value_func: (nStr) ->
            if typeof nStr is 'string'
              nStr.replace(/T/,' ')
            else
              nStr
  • ¶

    return ‘Date(‘+d.UTC()+’)’

  • ¶

    foreign_key: valuetype

        username: valuetype(cmp: (a, b) ->
          current_user = pndr.get_account_names()
    
          if true
            return 0   if a is b
            return -1  if a in current_user
            return 1   if b in current_user
            descending_cmp a, b
  • ¶

    else descending_cmp a, b

        )
        zero_to_one: valuetype(
          align: "right"
          type: "number"
          sort_onchange: false
          caster: parseFloat
          html_input:
            type: "range"
            min: 0
            max: 1
            step: "0.01"
            value: "0"
        )
    
      valuetype_factory_f = (vtf_spec) ->
  • ¶

    The valuetype_factory is a registry and generator of valuetypes. Some valuetypes are simple hardcoded things (at the moment!) but others might be elaborate gizmos which are self-referential to the current ponder or indeed they might rely on the contents of other ponders.

        vtf_spec = vtf_spec or {}
        vtf_spec.__proto__ = {}
        vtf_spec.valuetypes_by_id = {}
        self =
          ids: (args) ->
            out = []
            for i of vtf_spec.valuetypes_by_id
              out.push i
            out
          register_vt: (args) ->
            vbi = vtf_spec.valuetypes_by_id
            vbi[args.id] = args.vt
            args.vt.id args.id
          get: (vt_id) ->
            vtf_spec.valuetypes_by_id[vt_id]
          register_primitives: (args) ->
            for vt_id of primitive_valuetypes
              if primitive_valuetypes.hasOwnProperty(vt_id)
                vt = primitive_valuetypes[vt_id]
                self.register_vt
                  id: vt_id
                  vt: vt
        self.register_primitives()
        self
      valuetype_factory = valuetype_factory_f()
    
      valuetype_picker = (args) ->
        v = (if args.value then " value=\"" + args.value + "\"" else "")
        pre = ptw_vars.table_id + "__"
        onchange = ""
        picker_id = args.criterion.valuetype_picker_id()
        out = "<select id=\"" + picker_id + "\" name=\"" + v + "\">\n"
        out += "<option value=\"\"></option>\n"
        vt_ids = valuetype_factory.ids()
        for i of vt_ids
          k = vt_ids[i]
          sel = (if k is args.value + "" then " selected" else "")
          out += "<option" + sel + " value=\"" + k + "\">" + k + "</option>\n"
        out += "</select>\n"
        out
    
      qnames =
        wordnet: "http://wordnetweb.princeton.edu/perl/webwn?s="
        wiki: "http://en.wikipedia.org/wiki/"
    
      that =
        expand_qname: (input) ->
    
        get_next_criterion_id: (args) ->
          pndr.generate_id()
    
        get_next_candidate_id: (args) ->
          pndr.generate_id()
    
        is_hidden: ->
          ptw_vars.wgt_table.style.display is "none"
    
        hide: ->
          ptw_vars.wgt_table.style.display = "none"
    
        show: ->
          ptw_vars.wgt_table.style.display = "block"
    
        toggle_display: ->
          if pndr.is_hidden()
            pndr.show()
          else
            pndr.hide()
    
        update_windowShadeLabel: (args) ->
          num = ptw_vars.num_crit_shaded
          if num > 0
            ptw_vars.windowShadeLabel.innerHTML = Math.floor(num) + " more criteria"
          else
            ptw_vars.windowShadeLabel.innerHTML = ""
    
        reshade: (args) ->
  • ¶

    console.log(“reshade(): num_crit_shaded =”,ptw_vars.num_crit_shaded, “num_crit_shaded_this_drag =”,ptw_vars.num_crit_shaded_this_drag); visit each crit from the end if it should be hidden and it is not, hide it if it should not be hidden and it is, show it if it should be shown and it is, break

          num_to_shade = ptw_vars.num_crit_shaded
          num_crit = ptw_vars.criteria_by_row_idx.length
          num_shaded_so_far = 0
  • ¶

    count back from the end of the criteria

          crit_idx = num_crit - 1
          while crit_idx > -1
            shade_this_crit = num_shaded_so_far < num_to_shade
            crit = ptw_vars.criteria_by_row_idx[crit_idx]
            unless crit
              console.log "crit not found, skipping", crit_idx  if window.DEBUG_SHADING
              continue
            console.log "--------", crit_idx  if window.DEBUG_SHADING
            if shade_this_crit
              unless crit.is_hidden()
                console.log shade_this_crit, "  A  it is not hidden but it should be, so crit.hide()", crit.id(), crit_idx  if window.DEBUG_SHADING
                crit.hide()
                pndr.adjust_tds_widenedByUnshadedCritCount(-1)
  • ¶

    else it IS hidden so we do not need to hide it again

            else
              console.log "it should NOT be hidden"  if window.DEBUG_SHADING
  • ¶

    it should NOT be hidden

              if crit.is_hidden()
                console.log "but it IS, so show it"  if window.DEBUG_SHADING
  • ¶

    but it IS, so show it

                console.log shade_this_crit, "  B  it is hidden but should not be, so crit.show()", crit.id, crit_idx  if window.DEBUG_SHADING
                crit.show()
                pndr.adjust_tds_widenedByUnshadedCritCount(+1)
              else
  • ¶

    and it IS NOT HIDDEN, presumably like higher criteria, so…

                console.log shade_this_crit, "  C  it is not hidden, and should not be, so skip", crit.id, crit_idx  if window.DEBUG_SHADING
            num_shaded_so_far++
            crit_idx--
    
        navigate_to_ponder: (pndr_id) ->
          params =
            pndr_id: pndr_id
            origin_url: Ponderate.canonical_origin_url
            show_edit_button: true
            being_edited: true
            add_current_page_as_candidate: true
          Ponderate.op_queue__push params
          pndr.hide()
          Ponderate.insert_a_ponder params
  • ¶

    pndr.empty_the_frame(); Ponderate.op_queue__drain();

          return
    
        choose_ponder_to_add_candidate_to_handler: (evt) ->
  • ¶
             a     .span      .td        .tr
    
          a_row = evt.target.parentNode.parentNode.parentNode
          a_cand = ptw_vars.candidates_by_row_idx[a_row.sectionRowIndex]
  • ¶

    alert(a_cand.label()+” chosen!”); a_cand.get_ponder_id_from_url();

          pndr.navigate_to_ponder a_cand.get_ponder_id_from_url()
    
        del_candidate_handler: (evt) ->
  • ¶
             button.td        .tr
    
          a_row = evt.target.parentNode.parentNode
          a_cand = ptw_vars.candidates_by_row_idx[a_row.sectionRowIndex]
          if confirm("delete " + a_cand.label() + "?")
            pndr.del_candidate a_cand
            false
    
        edit_candidate_link_handler: (evt) ->
  • ¶
             button.td        .tr
    
          a_row = evt.target.parentNode.parentNode
          a_cand = ptw_vars.candidates_by_row_idx[a_row.sectionRowIndex]
          args =
            label: a_cand.label()
            url: a_cand.url()
          args = pndr.prompt_for_candidate_url(args)
          unless args.url
            a_cand.delete_url()
          else
            a_cand.url args.url
          a_cand.update_cand_visible()
          true
    
        reverse_criterion_default_order_handler: (evt) ->
  • ¶

    console.log(‘rcdoh()’);

          a_row = evt.target.parentNode.parentNode
          a_crit = ptw_vars.criteria_by_row_idx[a_row.sectionRowIndex]
          a_crit.default_order -1 * a_crit.default_order()
          a_crit.update_crit_visible()
          pndr.sort_candidates_by_criteria()
    
        onblur_candidate_label_handler: (evt) ->
          a_row = evt.target.parentNode.parentNode
          a_cand = ptw_vars.candidates_by_row_idx[a_row.sectionRowIndex]
          target = get_event_target(evt)
  • ¶

    http://www.quirksmode.org/dom/w3c_html.html#t07

          unvetted_new_label = target.innerText or target.textContent
          unless unvetted_new_label is a_cand.label()
            if has_value(unvetted_new_label) and (unvetted_new_label isnt "")
              a_cand.label unvetted_new_label
              a_cand.update_cand_visible()
              return true
          false
    
        onblur_criterion_label_handler: (evt) ->
          a_row = evt.target.parentNode.parentNode
          a_crit = ptw_vars.criteria_by_row_idx[a_row.sectionRowIndex]
          target = get_event_target(evt)
  • ¶

    http://www.quirksmode.org/dom/w3c_html.html#t07

          unvetted_new_label = target.innerText or target.textContent
  • ¶

    console.log(‘saving any changes to label of ‘+a_crit.label());

          a_crit.draggable_thing().draggable = true # toggled off in onfocus_criterion_label_handler
          unless (unvetted_new_label) is a_crit.label()
            if has_value(unvetted_new_label) and (unvetted_new_label isnt "")
              a_crit.label unvetted_new_label
              a_crit.update_crit_visible()
              return true
          false
    
        onfocus_criterion_label_handler: (evt) ->
  • ¶

    Turn off draggable so that editing can work properly, this is matched in onblur_criterion_label_handler by the restoration of draggability.

          a_row = evt.target.parentNode.parentNode
          a_crit = ptw_vars.criteria_by_row_idx[a_row.sectionRowIndex]
  • ¶

    toggled on in onfocus_criterion_label_handler

          a_crit.draggable_thing().draggable = false
          evt.target.focus()
    
        del_candidate: (a_cand) ->
  • ¶

    alert(‘deleting ‘+a_cand.label());

          a_cand.remove()
    
        add_candidate_handler: (evt) ->
          if ptw_vars.add_candidate_url
            window.location.assign ptw_vars.add_candidate_url
            true
          else
            pndr.has_unsaved_changes true
            pndr.add_candidate row_idx: 0
    
        reindex_candidates_by_row: (args) ->
          for id of ptw_vars.candidates_by_their_id
            if ptw_vars.candidates_by_their_id.hasOwnProperty(id)
              cand = ptw_vars.candidates_by_their_id[id]
              cand.register_cand()
  • ¶

    FIXME THIS IS NOT IN USE

        reindex_criteria_by_row: (args) ->
          for id of ptw_vars.criteria_by_their_id
            if ptw_vars.criteria_by_their_id.hasOwnProperty(id)
              crit = ptw_vars.criteria_by_their_id[id]
              crit.register_crit()
    
        prompt_for_candidate_url: (args) ->
          prmpt = (if is_undefined(args.url) then "" else args.url)
          retval = prompt("URL for '" + args.label + "'", prmpt)
          args.url = retval  if retval isnt null
          delete args.url  if is_undefined(args.url)
          args
    
        add_candidate: (args) ->
  • ¶

    console.log(“adding”,args);

          args = {}  unless args
          args.id = pndr.get_next_candidate_id()  unless args.id
          if ptw_vars.candidates_by_their_id[args.id]
            return null
          if not args.label or args.vet_label
            args.label = prompt("Enter label for new candidate", (if args.label then args.label else ""))
            return null  unless args.label
            args = pndr.prompt_for_candidate_url(args)  if not args.url or args.vet_url
            ensure_uniqueness = true
          if ensure_uniqueness
            found_by_label = pndr.find_candidate_by_label[args.label]
            found_by_url = pndr.find_candidate_by_url[args.url]
            if found_by_label or found_by_url
              msg = (if found_by_label then "a candidate of that name already exists" else "a candidate with that url already exists")
  • ¶

    scroll to found and hilight it

              pndr.show_message msg
              found_thing = found_by_label or found_by_url
  • ¶

    console.log(“tr:”,found_thing.tr);

          c = candidate(args)
          ptw_vars.candidates_by_their_id[c.id()] = c
          c.add_cand_to_table args
          pndr.reindex_candidates_by_row()  if c.idx() is 0
          c
    
        find_candidate_by_label: (label) ->
          if not ptw_vars.candidates_by_label and ptw_vars.candidates_by_row_idx.length > 0
            pndr.reindex_candidates_by_row()
          ptw_vars.candidates_by_label[label]
    
        find_candidate_by_url: (url) ->
          if not ptw_vars.candidates_by_their_id and ptw_vars.candidates_by_row_idx.length > 0
            pndr.reindex_candidates_by_row()
          ptw_vars.candidates_by_their_id[url]
    
        add_criterion_handler: (args) ->
          if ptw_vars.add_criterion_url
            window.location.assign ptw_vars.add_criterion_url
            true
          else
            pndr.has_unsaved_changes true
            pndr.add_criterion args
    
        add_criterion: (args) ->
          args = {}  unless args
          args.id = pndr.get_next_criterion_id()  unless args.id
          unless args.label
            args.label = prompt("Enter label for new criterion: eg. Happiness")
            return null  unless args.label
          c = criterion(args)
          ptw_vars.criteria_by_their_id[c.id()] = c
          c.add_crit_to_table()
          pndr.push_down_criteria except: c
          pndr.add_column_to_candidates crit_obj: c
          pndr.adjust_tds_widenedByCritCount +1
          c
    
        get_criterion_by_id: (crit_id) ->
          ptw_vars.criteria_by_their_id[crit_id]
    
        get_criterion_by_idx: (crit_idx) ->
          ptw_vars.criteria_by_row_idx[crit_idx]
    
        get_candidate_by_id: (cand_id) ->
          ptw_vars.candidates_by_their_id[cand_id]
    
        get_datum_by_cand_id_and_crit_id: (cand_id, crit_id) ->
          cand = pndr.get_candidate_by_id(cand_id)
          if cand?
            return cand.get_datum_by_crit_id(crit_id)
          return
    
        getElementsByClassName: (class_name, tag_hint) ->
          if ptw_vars.wgt_frame.getElementsByClassName
            ptw_vars.wgt_frame.getElementsByClassName class_name
          else
            gEBCN ptw_vars.wgt_frame, class_name, tag_hint
    
        getElementById: (id) ->
          ptw_vars.wgt_table.getElementsById id
    
        adjust_tds_widenedByCritCount: (incr) ->
  • ¶

    Ideally a selector for class widenedByCritCount would pick the things to have their colspans incremented or decremented

          widen_these = pndr.getElementsByClassName("widenedByCritCount")
          for i of widen_these
            a_td = widen_these[i]
            a_td.colSpan += incr
          pndr.adjust_tds_widenedByUnshadedCritCount(incr)
    
        adjust_tds_widenedByUnshadedCritCount: (incr) ->
  • ¶

    Ideally a selector for class widenedByCritCount would pick the things to have their colspans incremented or decremented

          widen_these = pndr.getElementsByClassName("widenedByUnshadedCritCount")
          for i of widen_these
            a_td = widen_these[i]
            a_td.colSpan += incr
    
        add_datum: (args) ->
          if typeof (args) is typeof ([])
            orig_args = args
            args =
              cand_id: args[0]
              crit_id: args[1]
              value: args[2]
          the_cand = ptw_vars.candidates_by_their_id[args.cand_id]
          unless the_cand
            console.log "candidate not found for datum:", args
            return
          the_datum = datum(args)
          the_cand.add_datum_to_candidate the_datum
          the_crit = ptw_vars.criteria_by_their_id[args.crit_id]
          if the_crit
            the_crit.remember_datum the_datum
          else
            console.log "criterion not found for datum:", args
  • ¶

    TODO scan the candidate for impact of new datum on any formulae???

        set_some_option: (evt) ->
  • ¶

    update ptw_vars.readable_by from the select

          ptw_vars[evt.srcElement.name] = evt.srcElement.value
    
        dump_criteria_positions: (say) ->
  • ¶

    console.log(“============” + “\n”+say);

          for i of ptw_vars.criteria_by_row_idx
            crit_obj = ptw_vars.criteria_by_row_idx[i]
            console.log "    ", crit_obj.label(), "sectionRowIndex", crit_obj.tr().sectionRowIndex + " i=" + i
    
        move_criterion_to_index: (movee, dest_index) ->
  • ¶

    pndr.dump_criteria_positions(‘before’);

          earlier_tr = undefined
          start_index = undefined
          next_crit = undefined
          next_earlier_tr = undefined
          final_index = dest_index
          i = undefined
          if movee.idx() < final_index
  • ¶

    console.log(“movee.idx() < final_index, ie %s < %s”, movee.idx(),final_index);

            earlier_tr = movee.tr()
            start_index = movee.idx()
            next_crit = null
            i = start_index
            while i <= final_index
              next_crit = ptw_vars.criteria_by_row_idx[i]
              next_earlier_tr = next_crit.tr()
  • ¶

    console.log(‘ bailing because crit not found for ‘+i);

              continue  unless next_crit
              next_crit.tr earlier_tr
              next_crit.update_crit_visible()
              earlier_tr = next_earlier_tr
              i++
            movee.tr earlier_tr
            movee.update_crit_visible()
          if movee.idx() > final_index
  • ¶

    console.log(“movee.idx() > final_index, ie %s > %s”, movee.idx(),final_index);

            earlier_tr = movee.tr()
            start_index = dest_index
            final_index = movee.idx()
            next_crit = null
            i = final_index
            while i >= start_index
              next_crit = ptw_vars.criteria_by_row_idx[i]
              next_earlier_tr = next_crit.tr()
  • ¶

    console.log(‘ bailing because crit not found for ‘+i);

              continue  unless next_crit
              next_crit.tr earlier_tr
              next_crit.update_crit_visible()
              earlier_tr = next_earlier_tr
              i--
            movee.tr earlier_tr
            movee.update_crit_visible()
    
        drop_criterion: (evt) ->
  • ¶

    assumes the dragged elem was a td inside the tr with an id linking it to a criterion

          ri = evt.srcElement.parentElement.rowIndex
          crit_rows = ptw_vars.wgt_table.tHead.children
          last_criterion_row_idx = crit_rows.length - 2
          ni = pndr.find_new_index_among(evt, crit_rows, 0, last_criterion_row_idx)
  • ¶

    console.log(“ni=”+ni+” ri=”+ri);

          return false  if is_undefined(ni) # see ref#6
          dragged_crit = ptw_vars.criteria_by_row_idx[ri]
  • ¶

    alert(“dropped on self”);

          return false  if ri is ni
          unless ni is ri
            pndr.move_criterion_to_index dragged_crit, ni
            pndr.move_crit_data_from_to_ dragged_crit, ri, ni
  • ¶

    alert(ptw_vars.criteria_by_row_idx[ni].label());

          pndr.sort_candidates_by_criteria()
          true
    
        move_crit_data_from_to_: (moved_crit, from_idx, to_idx) ->
          i = 0
          while i < ptw_vars.candidates_by_row_idx.length
            ptw_vars.candidates_by_row_idx[i].move_crit_datum_from_to_ moved_crit, from_idx, to_idx
            i++
    
        find_new_index_among: (evt, rows, first_idx, last_idx) ->
          diff = document.body.scrollTop - document.body.clientTop
          currentY = evt.clientY - diff
          retval = undefined
          row_idx = first_idx
    
          while row_idx <= last_idx
            row = rows[row_idx]
            row_top = row.offsetTop + row.offsetParent.offsetTop + row.offsetHeight
            row_bot = row_top + row.offsetHeight
  • ¶

    console.log(“for row %s is %s <= %s <= %s?”, row_idx,row_top,currentY,row_bot);

            return 0  if currentY < row_top
            if currentY >= row_top and currentY <= row_bot
  • ¶

    console.log(“yes!!”); console.log(evt,first_idx,last_idx,” ==> “,row_idx);

              retval = Math.max(0, row_idx - 1)
              break
            row_idx++
          retval = Math.max(row_idx - 1, 0)  unless is_defined(retval)
          retval = Math.min(retval, pndr.get_num_crit_unshaded() - 1)
          retval
    
        set_editing: (evt) ->
          ptw_vars.being_edited = ((if evt.srcElement then evt.srcElement else evt.target)).checked
          pndr.update_all_data_display()
          pndr.set_visibility_of_controls() #ptw_vars.being_edited);
          pndr.sort_candidates_by_criteria()  unless ptw_vars.being_edited
    
        install_stylesheet: (args) ->
  • ¶

    http://www.howtocreate.co.uk/tutorials/javascript/domcss

    http://dev.opera.com/articles/view/dynamic-style-css-javascript/ Not working great on IE

          sheet = document.createElement("style")
          ptw_vars.dynamic_stylesheet = sheet
          ptw_vars.wgt_table.parentNode.insertBefore sheet, ptw_vars.wgt_table
          pndr.insert_dynamic_stylesheet_rule [".authorizedUsersOnly", "{display:none;}"]
    
        insert_dynamic_stylesheet_rule__IE_version: (rule) ->
          dss = ptw_vars.dynamic_stylesheet.styleSheet
          dss.addRule rule[0], rule[1], dss.rules.length
  • ¶

    alert(rule);

        insert_dynamic_stylesheet_rule__standard_version: (rule) ->
          dssheet = ptw_vars.dynamic_stylesheet.sheet
          dssheet.insertRule rule[0] + " " + rule[1], dssheet.cssRules.length
    
        insert_dynamic_stylesheet_rule: (rule) ->
          dss = ptw_vars.dynamic_stylesheet
          if dss.sheet
            pndr.insert_dynamic_stylesheet_rule__standard_version rule
  • ¶

    IE

          else pndr.insert_dynamic_stylesheet_rule__IE_version rule  if dss.styleSheet
    
        containing_frame: ->
          ptw_vars.wgt_frame
    
        make_caption_draggable_and_editable: ->
  • ¶

    $(ptw_vars.caption_elem).click(); console.log “make_caption_draggable_and_editable()”

          try
            $(ptw_vars.wgt_frame).draggable(handle: ptw_vars.caption_elem).click(->
              $(ptw_vars.wgt_frame).draggable disabled: false
            ).dblclick ->
              $(ptw_vars.wgt_frame).draggable disabled: true
    
          catch e
            throw ("patched jquery-ui expects \"Ponderate.jQuery = jQuery\" for embed.js")
  • ¶

    $(pndr.containing_frame()).draggable({handle:ptw_vars.caption_elem}); console.log(‘/make_caption_draggable_and_editable()’);

        widget: ->
          ptw_vars.wgt_table
    
        set_style_on_widget_with: (rules) ->
          style = pndr.transform_rules_to_string(rules)
  • ¶

    console.log(“setting style on widget to”,style); pndr.widget().setAttribute(‘style’,”{“+pndr.transform_rules_to_string(rules)+”}”);

          pndr.widget().setAttribute "style", style
    
        transform_rules_to_string: (rules) ->
          def = ""
          i = 0
    
          while i < rules.length
            rule = rules[i]
            def += rule[0] + rule[1] + " "
            i++
          def
    
        replace_style_in_containing_div_with: (rules) ->
  • ¶

    http://www.phpied.com/dynamic-script-and-style-elements-in-ie/

          container = pndr.containing_frame()
          old_styles = pndr.getElementsByClassName("ptwDynamicStyle", "style")
          osi = old_styles.length - 1
    
          while osi > -1
            os = old_styles[osi]
  • ¶

    console.log(“removeChild”,os);

            container.removeChild os  if os
            osi--
          ss1 = document.createElement("style")
          ss1.setAttribute "type", "text/css"
          ss1.setAttribute "class", "ptwDynamicStyle"
          def = pndr.transform_rules_to_string(rules)
          container.insertBefore ss1, container.childNodes[0]
          if ss1.styleSheet # IE
            ss1.styleSheet.cssText = def
          else
            ss1.appendChild document.createTextNode(def)
    
        replace_dynamic_stylesheet_with: (rules) ->
  • ¶

    http://dev.opera.com/articles/view/dynamic-style-css-javascript/

          pndr.empty_dynamic_stylesheet()
          i = undefined
          dss = ptw_vars.dynamic_stylesheet
  • ¶

    console.log(“dss:”,dss,”cssRules:”,dss.cssRules,”rules:”,dss.rules); console.log(dss,ptw_vars.dynamic_stylesheet);

          if dss.sheet # the standard
            i = 0
            while i < rules.length
              rule = rules[i]
              pndr.insert_dynamic_stylesheet_rule__standard_version rule
              i++
          else if dss.styleSheet # IE
            i = 0
            while i < rules.length
              pndr.insert_dynamic_stylesheet_rule__IE_version rules[i]
              i++
          else
            pndr.submit_bug_report
              method: "pndr.replace_dynamic_stylesheet_with"
              problem: "browser lacks dss.sheet and dss.styleSheet, not standard and not IE"
              severity: "user experience affected"
    
        empty_dynamic_stylesheet: (args) ->
          dss = ptw_vars.dynamic_stylesheet
          if dss.sheet # the standard
            dss.sheet.deleteRule 0  while dss.sheet.cssRules.length > 0
          else if dss.styleSheet # IE
            dss.styleSheet.removeRule 0  while dss.styleSheet.rules.length > 0
          else
            pndr.submit_bug_report
              method: "pndr.empty_dynamic_stylesheet"
              problem: "browser lacks dss.sheet and dss.styleSheet, not standard and not IE"
              severity: "user experience affected"
    
        set_disabled_of_stylesheet_by_name: (stylesheet_name, disabled) ->
          i = 0
          while i < document.styleSheets.length
            ss = document.styleSheets[i]
  • ¶

    alert(ss.title); console.log(ss); alert(ss.href);

            if ss.title is stylesheet_name or (ss.href and ss.href.indexOf(stylesheet_name) > -1)
              alert ss.title + " was " + ((if ss.disabled then "disabled" else "")) + " and now is " + ((if disabled then "disabled" else ""))
              ss.disabled = disabled
            i++
    
        set_visibility_of_controls: (args) ->
  • ¶

    console.log(ptw_vars.wgt_table); alert(“set_visibility_of_controls”);

          i = undefined
          dynamic_style = ""
          if true or is_IE
            editButtons = pndr.getElementsByClassName("editButton")
            i = 0
            while i < editButtons.length
              editButtons[i].style.display = (if ptw_vars.show_edit_button then "inline" else "none")
              i++
  • ¶

    editButtons[i].style.cssText = ptw_vars.show_edit_button ? “”:display_none;

            editControls = pndr.getElementsByClassName("editControl")
            i = 0
            while i < editControls.length
              editControls[i].style.display = (if ptw_vars.being_edited then "" else "none")
              i++
  • ¶

    editControls[i].style.cssText = ptw_vars.being_edited ? “”:display_none;

          else
            rules = []
  • ¶

    alert(“ptw_vars.being_edited:”+ptw_vars.being_edited+” ptw_vars.show_edit_button:”+ptw_vars.show_edit_button);

            rules.push [".editControl", display_none]  unless ptw_vars.being_edited
  • ¶

    rules.push([“caption:before”,’{content:”NOT being_edited”;font-size:8px;}’]);

            rules.push [".editButton", "{display:none;}"]  unless ptw_vars.show_edit_button
  • ¶

    rules.push([“caption:after”,’{content:”DO NOT show_edit_button”;font-size:8px;}’]);

  • ¶

    pndr.replace_style_in_containing_div_with(rules);

            pndr.set_style_on_widget_with rules
  • ¶

    pndr.replace_dynamic_stylesheet_with(rules);

          editables = pndr.getElementsByClassName("contentEditableInEditMode")
          i = 0
          while i < editables.length
            editables[i].contentEditable = ptw_vars.being_edited
            i++
          alterables = pndr.getElementsByClassName("contentAlterableInEditMode")
          i = 0
          while i < alterables.length
            if ptw_vars.being_edited
              alterables[i].draggable = false
  • ¶

    alterables[i].onclick = pndr.display_criterion_valuetype_selector;

            else
              alterables[i].onclick = null
            i++
          them = pndr.getElementsByClassName("valuetypeId")
          i = 0
          while i < them.length
            if ptw_vars.being_edited
              them[i].draggable = false
              them[i].onclick = pndr.display_criterion_valuetype_selector
            else
              them[i].onclick = null
            i++
    
        update_all_data_display: (args) ->
          i = undefined
          for i of ptw_vars.candidates_by_row_idx
  • ¶

    console.log(i);

            ptw_vars.candidates_by_row_idx[i].update_candidate_display()
    
        discover_pndr8_title: (args) ->
          captions = ptw_vars.wgt_table.getElementsByTagName("caption")
          pndr.pndr8_title captions[0].innerText or captions[0].textContent  if captions
          pndr.pndr8_title()
    
        pndr8_title: (s) ->
          if typeof (s) is typeof ("")
            ptw_vars.pndr8_title = s
            captions = ptw_vars.wgt_table.getElementsByTagName("caption")
            if captions.length
  • ¶

    console.log(“captions.innerText” , typeof captions.innerText);

              if typeof captions[0].innerText isnt "undefined"
                captions[0].innerText = s
              else
                captions[0].textContent = s
          ptw_vars.pndr8_title
    
        submit_bug_report: (report) ->
          if ptw_vars.submitted_bugs.indexOf(report) is -1
            ptw_vars.submitted_bugs.push report
            report.platform = navigator.platform
            report.userAgent = navigator.userAgent
            report.location = window.location
            pndr.send_generic_json_to_server JSON.stringify(report), "PUT", pndr.ensure_absolute_url("/bug_report/")
    
        ensure_absolute_url: (path) ->
          console.log "ORIGIN_URL", ptw_vars.origin_url
          ptw_vars.origin_url = window.location.origin  if typeof ptw_vars.origin_url is "undefined"
          console.log "ORIGIN_URL", ptw_vars.origin_url, "FORCED"
          resp = (if (path[0] is "/") then ptw_vars.origin_url + path else path)
          resp
    
        generate_id: (typ) ->
          return "a_random_oid"
    
        save_all: (evt) ->
          out = {}
          pndr.discover_pndr8_title()
          t = pndr.pndr8_title()
          if t isnt `undefined` and t isnt ""
            out["name"] = t
          else
            out["name"] = "no name"
          out["candidates"] = pndr.candidates_for_saving()
          out["criteria"] = pndr.criteria_for_saving()
          out["data"] = pndr.candidate_data_for_saving()
          out["writeable_by"] = ptw_vars.writeable_by
          out["readable_by"] = ptw_vars.readable_by
          out["created_by"] = ptw_vars.created_by
          json_to_save = JSON.stringify(out)
          console.log json_to_save
          verb = undefined
          path = "/pndr8n"
          if pndr.ponderation_id()
            verb = "PUT" # there IS an ID, so we are saving
            path += "/" + pndr.ponderation_id()
          else
            verb = "POST" # there IS NOT and ID, so we are creating
          url = pndr.ensure_absolute_url(path)
          console.log verb, url
          pndr.send_json_to_server json_to_save, verb, url
    
        get_session_id: ->
  • ¶

    console.log(“get_session_id”,document.cookie);

          document.cookie
  • ¶
    if (ptw_vars.session_id){
    console.log("get_session_id",document.cookie);
    return ptw_vars.session_id;
    }
    return "no session_id";
    
        send_json_to_server: (json_to_save, verb, url) ->
          handler = ->
  • ¶

    console.log(“readyState:”,AJAX.readyState,” status:”,AJAX.status, “ responseText:”,AJAX.responseText);

            if AJAX.readyState is 4 and AJAX.status is 204
              pndr.has_unsaved_changes false
  • ¶

    var json = eval( AJAX.responseText +”;”);

            else if AJAX.readyState is 4 and AJAX.status is 201 #
  • ¶

    http://en.wikipedia.org/wiki/List_of_HTTP_status_codes

              new_location = AJAX.responseText
  • ¶

    the Location header should suffice, but XMLHttpRequest does not permit access to the headers, so new location in body of the response

              window.location.assign new_location
            else console.log "Status != 20x... status=" + AJAX.status  if AJAX.readyState is 4 and (AJAX.status < 200 or AJAX.status > 299)
            console.log AJAX.status, AJAX.statusText, AJAX  if AJAX.readyState is 4
    
          AJAX = pndr.createXMLHttpRequest()
          show = ->
            AJAX.onreadystatechange = handler
            AJAX.open verb, url
            AJAX.withCredentials = "true"
            console.log "AJAX.open", verb, url
            AJAX.setRequestHeader "Content-type", "application/json"
            AJAX.withCredentials = "true" # can not happen earlier
            AJAX.send json_to_save
    
          show()
    
        send_generic_json_to_server: (json_to_save, verb, url) ->
          handler = ->
            if AJAX.readyState is 4 and AJAX.status is 200 # OK
              console.log AJAX.responseText
            else if AJAX.readyState is 4 or AJAX.status is 204 # No Content
              console.log AJAX.responseText
            else if AJAX.readyState is 4 and AJAX.status is 201 # Created
  • ¶

    http://en.wikipedia.org/wiki/List_of_HTTP_status_codes

              new_location = AJAX.responseText
  • ¶

    the Location header should suffice, but XMLHttpRequest does not permit access to the headers, so new location in body of the response

              window.location.assign new_location
            else alert "Status != 20x... status=" + AJAX.status  if AJAX.readyState is 4 and (AJAX.status < 200 or AJAX.status > 299)
            console.log AJAX.status, AJAX.statusText, AJAX  if AJAX.readyState is 4
    
          AJAX = pndr.createXMLHttpRequest()
          show = ->
            AJAX.onreadystatechange = handler
            AJAX.open verb, url
            AJAX.setRequestHeader "Content-type", "application/json"
            AJAX.withCredentials = "true" # can not happen earlier
            AJAX.send json_to_save
    
          show()
    
        make_absolute_url: (url) ->
          return pndr.get_ponderate_origin_url() + url  if url[0] is "/"
          url
    
        fill_widget_with_json_from_url: (args) ->
          verb = "GET"
          json_to_send = ""
          resp_json = undefined
          url = args.src
          handler = ->
  • ¶

    try {

            if AJAX.readyState is 4 and AJAX.status is 200 # OK
  • ¶

    console.log(AJAX.responseText); eval(“resp_json = “+ AJAX.responseText+”;”);

              args.ponderspec = JSON.parse(AJAX.responseText)
              pndr.document_write_widget()
              pndr.fill_widget_from_ponderspec args
              pndr.sort_candidates_by_criteria args
              pndr.set_visibility_of_controls() # TRIAL
            console.log AJAX.status, AJAX.statusText, AJAX  if AJAX.readyState is 4
  • ¶

    } catch(e){console.log(“error”,e);};

          AJAX = pndr.createXMLHttpRequest()
          hit = ->
  • ¶

    try {

            AJAX.onreadystatechange = handler
            BLOCK = false
            ASYNC = true
            AJAX.open verb, url, BLOCK
  • ¶
                         FireFox breaks on AJAX.withCredentials, but it is needed at bookmarking time
    
  • ¶

    console.log(“AJAX.withCredentials:”,AJAX.withCredentials);

            if typeof AJAX.withCredentials isnt "undefined"
  • ¶

    can not happen before AJAX.open

              try
                AJAX.withCredentials = "true"
              catch e
                pndr.show_message "Please try a better supported browser: " + SUPPORTED_BROWSERS, 15
                console.log "failure to fetch authenticated data because", e
            if json_to_send
              AJAX.setRequestHeader "Content-type", "application/json"
              AJAX.send json_to_save
            else
              AJAX.send()
  • ¶

    } catch(e){console.log(“error”,e);};

          hit()
    
        createXMLHttpRequest: ->
  • ¶

    FIXME switch over to $.ajax it handles cross-browser issues

  • ¶

    See http://en.wikipedia.org/wiki/XMLHttpRequest Provide the XMLHttpRequest class for IE 5.x-6.x:

          if typeof XMLHttpRequest is "undefined"
            xMLHttpRequest = ->
              try
                return new ActiveXObject("Msxml2.XMLHTTP.6.0")
              try
                return new ActiveXObject("Msxml2.XMLHTTP.3.0")
              try
                return new ActiveXObject("Msxml2.XMLHTTP")
              try
                return new ActiveXObject("Microsoft.XMLHTTP")
              throw new Error("This browser does not support XMLHttpRequest.")
          else
            xMLHttpRequest = XMLHttpRequest
          xhr = new xMLHttpRequest()
  • ¶
    if (xhr.withCredentials){
    xrh.withCredentials = "true";
    }
    
          xhr
    
        criteria_for_saving: (args) ->
          out = []
          for i of ptw_vars.criteria_by_row_idx
            out.push ptw_vars.criteria_by_row_idx[i].pack_crit_for_saving()
          out
    
        candidates_for_saving: (args) ->
          out = []
          for i of ptw_vars.candidates_by_row_idx
            out.push ptw_vars.candidates_by_row_idx[i].pack_cand_for_saving()
          out
    
        candidate_data_for_saving: (args) ->
          out = []
          for i of ptw_vars.candidates_by_row_idx
            out = out.concat(ptw_vars.candidates_by_row_idx[i].data_for_saving())
          out
    
        display_criterion_valuetype_selector: (evt) ->
          a_row = evt.target.parentNode.parentNode
          a_crit = pndr.get_criterion_by_idx(a_row.sectionRowIndex)
          return a_crit.display_valuetype_selector()
    
        set_select_to_value: (select_obj, to_value) ->
          index = 0
    
          while index < select_obj.children.length
            if select_obj.children[index].value is to_value + ""
              select_obj.selectedIndex = index
              alert "set select to " + to_value
              return true
            index++
          false
    
        set_criterion_valuetype_handler: (evt) ->
          criterion_id = evt.srcElement.id.split("__")[0]
          criterion_obj = ptw_vars.criteria_by_their_id[criterion_id]
  • ¶

    console.log(evt,criterion_id);

          if (is_defined(criterion_obj.valuetype_id())) and (criterion_obj.num_of_candidates_with_non_defaulted_data() > 0) and criterion_obj.data_will_be_lost_if_valuetype_changes_to(evt)
            unless confirm("Data might be lost, is that OK?")
              pndr.set_select_to_value evt.srcElement.value, criterion_obj.valuetype_id()
              criterion_obj.update_crit_visible()
              return false
  • ¶

    these 2 lines meant to turn of second pass thru this handler…

          evt.target.onblur = null
          evt.target.onchange = null
          criterion_obj.valuetype_id evt.target.value
          criterion_obj.discover_valuetype()
          criterion_obj.update_crit_visible()
          pndr.update_column_for_candidates criterion_obj
          true
    
        get_datum_by_input_element_id: (input_element_id) ->
          splits = input_element_id.split("__")
          crit_id = splits[0]
          crit_obj = ptw_vars.criteria_by_their_id[crit_id]
          cand_id = splits[1]
          cand_obj = ptw_vars.candidates_by_their_id[cand_id]
          cand_obj.datum_for_crit_id(crit_id)
    
        set_datum_value: (evt) ->
          a_datum = pndr.get_datum_by_input_element_id(evt.srcElement.id)
          a_datum.set_value evt.target.value
    
        update_column_for_candidates: (criterion_obj) ->
          for i of ptw_vars.candidates_by_row_idx
            cand_obj = ptw_vars.candidates_by_row_idx[i]
            d_obj = cand_obj.ensure_datum_for_criterion(criterion_obj)
            d_obj.update_datum_display()
    
        populate_a_select: (args) ->
          var_name = args["select_name"]
          select_id = var_name
          select_control = pndr.getElementsByClassName(var_name)[0] #.getElementById(select_id);
          k = undefined
          v = ptw_vars[var_name] # ie readable_by or writeable_by
          out = ""
          out += "<option value=\"\"></option>\n"
          for k of choosable_agent_types
            sel = ((if (k is v + "") then " selected" else ""))
  • ¶

    console.log(k,choosable_agent_types[k],sel);

            out += "<option" + sel + " value=\"" + k + "\">" + choosable_agent_types[k] + "</option>\n"
          select_control.innerHTML = out
    
        widget_id: (n) ->
          if n
            ptw_vars.widget_id = n
  • ¶

    console.log(‘ptw_vars.table_id:’+ptw_vars.table_id);

            ptw_vars.wgt_table = pndr.getElementsByClassName("ponderateTabularWidget")[0]
            wgt = ptw_vars.wgt_table
            captions = ptw_vars.wgt_table.getElementsByClassName("contentEditableInEditMode")
  • ¶

    if (captions) captions[0].contentEditable = true; console.log “widget_id()”

            ptw_vars.caption_elem = captions[0]
    
            if ptw_vars.make_table_caption_draggable
  • ¶

    how to make content editable AND draggable http://stackoverflow.com/a/10317527

              pndr.make_caption_draggable_and_editable()
            ptw_vars.candidate_rows = ptw_vars.wgt_table.tBodies[0]
            ptw_vars.criterion_rows = ptw_vars.wgt_table.tHead
            ptw_vars.windowshade = pndr.getElementsByClassName("windowShade")[0]
            ptw_vars.windowShadeLabel = pndr.getElementsByClassName("windowShadeLabel")[0]
  • ¶

    console.log “WS”, ptw_vars.windowshade

  • ¶

    ptw_vars.windowshade.ondrag = pndr.ondrag_windowshade;

  • ¶

    ptw_vars.windowshade.onclick = ptw_vars.ondrag_windowshade; $ = jquery;

            Ponderate.jQuery = $  unless Ponderate.jQuery
            pndr.initialize_windowshade()
  • ¶

    ptw_vars.controls_id = n+’controls’; var pre = ptw_vars.table_id + ‘‘;

            pndr.getElementsByClassName("addCriterionButton")[0].onclick = pndr.add_criterion_handler
            pndr.getElementsByClassName("addCandidateButton")[0].onclick = pndr.add_candidate_handler
            pndr.getElementsByClassName("saveButton")[0].onclick = pndr.save_all
            if must_attachEvent
              pndr.getElementsByClassName("editButton")[0].attachEvent "onclick", pndr.set_editing
              pndr.getElementsByClassName("readable_by")[0].attachEvent "onchange", pndr.set_some_option
              pndr.getElementsByClassName("writeable_by")[0].attachEvent "onchange", pndr.set_some_option
            else
              pndr.getElementsByClassName("editButton")[0].onclick = pndr.set_editing
              pndr.getElementsByClassName("readable_by")[0].onchange = pndr.set_some_option
              pndr.getElementsByClassName("writeable_by")[0].onchange = pndr.set_some_option
            ptw_vars.wgt_controls = pndr.getElementsByClassName("ptwControls")[0]
  • ¶

    pndr.set_visibility_of_controls();

          ptw_vars.widget_id
    
        initialize_windowshade: ->
          shade_grain_px = 28
          $(ptw_vars.windowshade).draggable
            axis: "y"
            grid: [shade_grain_px, shade_grain_px]
            start: (event, ui) ->
              console.clear()
  • ¶

    set to zero if no value (or already zero!)

              ptw_vars.num_crit_shaded = 0  unless ptw_vars.num_crit_shaded
  • ¶

    we keep track of how many criteria have been shaded during this drag

              ptw_vars.num_crit_shaded_this_drag = 0
  • ¶

    and how much that number has changed since last ‘drag:’ was called

              ptw_vars.num_crit_shaded_this_drag_last = 0
    
            stop: (event, ui) ->
  • ¶

    console.log “stop() num_crit_shaded:”, ptw_vars.num_crit_shaded

              pndr.sort_candidates_by_criteria()
              $(event.target).css "top", "0px"
              ptw_vars.num_crit_shaded_this_drag = 0
    
            drag: (event, ui) ->
  • ¶

    console.log(“drag() num_crit_shaded:”,ptw_vars.num_crit_shaded); console.log(event,ui);

              number_shaded = 0
  • ¶

    Record the current state of num_crit_shaded_this_drag

              ptw_vars.num_crit_shaded_this_drag_last = ptw_vars.num_crit_shaded_this_drag
  • ¶

    Think about this as being about shading (rather than about showing). As a consequence we apply the multiplier of -1 (to set that polarity).

              crit_shaded_this_drag = ui.position.top / shade_grain_px * -1
              num_crit = pndr.get_num_criteria()
  • ¶

    If dragging up overshoots the top stop at shading all

              if crit_shaded_this_drag > num_crit
                crit_shaded_this_drag = num_crit
              ptw_vars.num_crit_shaded_this_drag = crit_shaded_this_drag
  • ¶

    console.log “crit_shaded_this_drag”, crit_shaded_this_drag

  • ¶

    keep the windowShade from drifting away from its row

              ui.position.top = 0
  • ¶

    any motion since last call to drag has been below threshold, so…

              if ptw_vars.num_crit_shaded_this_drag_last is ptw_vars.num_crit_shaded_this_drag
                return
  • ¶

    So, if we get to here then there has been a change in the number of criteria the user wants to shade.

              delta = ptw_vars.num_crit_shaded_this_drag - ptw_vars.num_crit_shaded_this_drag_last
              ptw_vars.num_crit_shaded += delta
  • ¶

    If drag overshoots, max out at the number of crits

              if ptw_vars.num_crit_shaded > ptw_vars.criteria_by_row_idx.length
                ptw_vars.num_crit_shaded = ptw_vars.criteria_by_row_idx.length - 1
  • ¶

    If dragging down overshoots the bottom stop at zero to shade

              if ptw_vars.num_crit_shaded <= 0
                ptw_vars.num_crit_shaded = 0
    
              pndr.reshade()
              pndr.update_windowShadeLabel()
              return
    
    
        get_stylesheet_link: (SHEET_NAME) ->
          SHEET_NAME_patt = new RegExp("SHEET_NAME", "g")
          retval = pndr.replace_BASE_URL(ptw_vars.stylesheet_link_tmpl).replace(SHEET_NAME_patt, SHEET_NAME)
          retval
    
        get_ponderate_origin_url: ->
  • ¶

    return ptw_vars.origin_url; console.log(“WINDOW.LOCATION=”,window.location);

          Ponderate.canonical_origin_url
    
        replace_BASE_URL: (s) ->
          s.replace new RegExp("BASE_URL", "g"), pndr.get_ponderate_origin_url()
    
        replace_PNDR_ID: (s) ->
  • ¶

    This is VERY HACKY. It should work in the old Ponderate contexts and in Nooron’s new VisualizationController context where pndr_id is not set but table_id IS set. TODO clean this up!!!

          if ptw_vars.pndr_id
            retval = s.replace(new RegExp("PNDR_ID", "g"), ptw_vars.pndr_id)
          else
            retval = s.replace(new RegExp("ptw__PNDR_ID", "g"), ptw_vars.table_id)
          retval
    
        get_template_by_name: (template_name) ->
  • ¶

    WIP: the hit is happening but the callback never happens

          template_value = undefined
          console.log "get_template_by_name(\"" + template_name + "\")"
          $.ajax
            type: "GET"
            async: false
            url: "/templates/" + template_name
            dataType: "xml"
            success: (data) ->
              console.log "callback"
              template_value = data
    
          console.log "template_value =", template_value
          retval = pndr.replace_BASE_URL(pndr.replace_PNDR_ID(template_value))
          retval
    
        get_widget_html: (args) ->
          patt = new RegExp(ptw_defaults.table_id, "g")
          retval2 = pndr.replace_BASE_URL(pndr.replace_PNDR_ID(widget_html))
          retval2
    
        frame_id: (fid) ->
          ptw_vars.frame_id = fid  if is_defined(fid)
          ptw_vars.frame_id
    
        get_or_make_frame: ->
          if not ptw_vars.wgt_frame?
            ptw_vars.wgt_frame = document.getElementById(pndr.frame_id())
            unless ptw_vars.wgt_frame
              document.write "<div id=\"" + pndr.frame_id() + "\"></div>"
              ptw_vars.wgt_frame = document.getElementById(pndr.frame_id())
    
        document_write_widget: (args) ->
  • ¶

    document.write(pndr.get_stylesheet_link(‘writingOff’)); document.write(pndr.get_stylesheet_link(‘editingOff’)); document.write(pndr.get_stylesheet_link(‘tabular_widget’)); console.log “ptw_vars::”, ptw_vars, “pndr::”, pndr

          pndr.get_or_make_frame()
          ptw_vars.wgt_frame.innerHTML = pndr.get_widget_html()
  • ¶

    document.write(pndr.get_widget_html());

          ptw_vars.widget_id = ptw_vars.table_id
          pndr.widget_id ptw_vars.table_id
  • ¶

    is the following really needed here?!? ptw_vars.candidate_rows = ptw_vars.wgt_table.tBodies[0];

  • ¶

    pndr.install_stylesheet();

          pndr.set_visibility_of_controls()
          pndr.hide_or_show_footer()
    
        hide_or_show_footer: ->
          if ptw_vars.hide_footer
            sel = 'div#pndr_'+ptw_vars.pndr_id+', tfoot'
            $(sel).hide()
    
        show_message: (msg, timeout) ->
  • ¶

    FIXME show message somewhere ptw_vars.wgt_frame.getElementsByClassName(‘messageBox’)[0].innerHTML = msg; self.getElementsByClassName(‘messageBox’)[0].innerHTML = msg;

          console.log "SHOW_MESSAGE", msg  if msg
  • ¶

    pndr.getElementsByClassName(‘messageBox’)[0].innerHTML = ‘’;

        get_url_to_ponder_json: (pndr_id) ->
          ptw_vars.origin_url + "/pndr8/" + pndr_id + ".json"
    
        is_bookmarking_mode: (b) ->
          ptw_vars.is_bookmarking_mode = b  if is_boolean(b)
          ptw_vars.is_bookmarking_mode
    
        set_created_by: (literal) ->
         console.log "set_created_by",literal
         $('div#pndr_'+ptw_vars.pndr_id+' span.createdBy').html(literal)
    
        set_authenticated_user: (account_obj) ->
          ptw_vars.authenticated_user = account_obj
          ptw_vars.authenticated_username = account_obj.account_key
  • ¶

    Lets also keep a list of the various identifiers for the pserson to make it possible to detect them at sort time.

          ptw_vars.account_names = an = []
          if account_obj
            au = account_obj
            if au.displayName?
              an.push(au.displayName)
            if au.account_key?
              an.push(au.account_key) unless au.account_key in an
  • ¶

    Because who is logged on can affect the sorting of the username column of the /pndr/00000000000000 page we need to trigger a re-sort when ponderae_authenticated_user gets updated

          pndr.sort_candidates_by_criteria()
    
        get_account_names: () ->
          ptw_vars.account_names || []
    
        get_authenticated_username: ->
          ptw_vars.authenticated_username
    
        reject_old_browsers: ->
          if navigator.userAgent.match(/MSIE\s(?!9.0)/)
  • ¶

    This is a crude attempt to tell if we have a modern browser It should fail on IE < 9 and pass on Chrome, Safari and IE9

            document.body.innerHTML += "<h2>Sorry, Ponderate needs a more modern browser.  " + SUPPORTED_BROWSERS + " work best and IE9 can work for display.</h2>"
            true
          else
            false
    
        subscribe_widget_to_cmd: (args, cmd) ->
          add_candidates_from_frames = (details_list) ->
            i = 0
    
            while i < details_list.length
              frame_details = details_list[i]
              cand_spec =
                id: frame_details.handle
                label: frame_details.pretty_name
    
              pndr.add_candidate cand_spec
              i++
  • ¶

    args.noor.subscribe(cmd,add_candidates_from_frames);

          add_candidates_from_frames [
            handle: "bob"
            pretty_name: "Robert"
          ]
    
        make_widget_here: (args) ->
          args = args or {}
  • ¶

    console.log “make_widget_here:”, args.pndr_id, args

          return  if pndr.reject_old_browser
          copy_props_from_to_([
               "add_candidate_label", "add_candidate_url", "base_url",
               "show_edit_button", "pndr_id", "origin_url",
               "add_current_page_as_candidate", "being_edited",
               "show_edit_button", "readable_by", "writeable_by",
               "frame_id", "authenticated_username", "is_bookmarking_mode"],
                   args, ptw_vars)
    
          if args.noor
            pndr.show_message "loading data"
            args.src = "URL_FOR_THIS_PNDR"
            pndr.fill_widget_with_json_from_url args
            pndr.document_write_widget args
            pndr.subscribe_widget_to_cmd args,
              op: "get_kbs"
            pndr.show_message()
  • ¶

    pndr.populate_a_select({select_name: ‘writeable_by’}); pndr.populate_a_select({select_name: ‘readable_by’});

          else if args.pndr_id
  • ¶

    console.log “about to load data for pndr_id:”, args.pndr_id ptw_vars.pndr_id = args.pndr_id;

            pndr.show_message "loading data"
            args.src = pndr.get_url_to_ponder_json(args.pndr_id)
            pndr.fill_widget_with_json_from_url args
            pndr.show_message()
    
          else if args.simple_list
  • ¶

    pndr.pndr8_title = args.title

            pndr.document_write_widget(args)
    
          else
  • ¶

    new ponder

            ptw_vars.being_edited = true
            pndr.show_message "loading data"
            args.src = pndr.get_url_to_ponder_json(args.pndr_id)
  • ¶

    pndr.fill_widget_with_json_from_url(args);

            pndr.show_message()
            pndr.document_write_widget args
            pndr.populate_a_select select_name: "writeable_by"
            pndr.populate_a_select select_name: "readable_by"
  • ¶

    console.log(“args for new ponder”,args);

          pndr.pndr8_title args.title  if args.title
          pndr.add_current_page_as_candidate()  if ptw_vars.add_current_page_as_candidate
  • ¶

    console.log(“being_edited”,pndr.being_edited()); console.log(“show_edit_button”,ptw_vars.show_edit_button); console.log(“PTW”,ptw_vars); alert(“make_widget_here(ptw_vars=”+JSON.stringify(ptw_vars)+”)”);

          pndr
    
        add_current_page_as_candidate: (args) ->
          pndr.add_candidate
            row_idx: 0
            vet_label: true
            vet_url: true
            url: document.location.href
            label: document.title
    
        sort_candidates_if_unsorted: (args) ->
  • ¶

    console.log(‘sort_candidates_if_unsorted’);

          pndr.sort_candidates_by_criteria()  if pndr.candidates_unsorted()
    
        sort_candidates_by_criteria: (args) ->
          window.current_user_shown = false
  • ¶

    OK, this is relatively tricky. The goal is to sort the list of candidates by a table of data in columns given by the criteria. The objective is to perform a sort equivalent to an SQL grouped sort, so if the first column to sort on has equal values in it then the second column drives the sort and if they are equal then the third and so on.

          pndr.recursive_sort_rows_by_crit_list
  • ¶

    This routine is further complicated by the fact that we want to remain agnostic about where the ordered list of criteria (columns to sort by) are coming from. Initally the leftmost column is considered, the the second from the left, and so on, but we might want a different user experience to drive the specification of the ordering. Maybe the columns to sort on will be flagged with numbers inidcating their priority in the sequence, etc. But the abstraction boundry about that is HERE. So the order of the ‘criteria:’ is what will drive the search.

            criteria: ptw_vars.criteria_by_row_idx
  • ¶

    The items to be moved are actually the TR elements in the TRs argument.

            trs: ptw_vars.candidate_rows
  • ¶

    Furthermore the data is in one structure (the ‘candidates:’) but the things to be moved are actually the TRs in the ‘TRs’ variable. In particular, the data are in the candidates, accessible using get_datum_by_crit_id().

            candidates: ptw_vars.candidates_by_row_idx
    
    
        recursive_sort_rows_by_crit_list: (args) ->
  • ¶

    So, the sort has been specified ouside of here. The question now is how to accomplish it. Here is what we’ll do: everything we can to make it fast. We will build a grand_comparitor function which is composed of the comparitor functions provided by each of the criteria, because (of course) the criteria have associated ‘valuetypes’ which can be very strange indeed, having nonalphanumeric sorting properties like ‘severe’ > ‘moderate’ > ‘trivial’ or ‘gale’ > ‘breeze’. The criterion-provided comparitors will also embody the native ‘ASCending’ vs ‘DESCending’ default associated with that valuetype. The windspeed criterion would default to ‘ASC’ while letter_grade would default to ‘DESC’ and dollars would default to ‘ASC’. The comparitor will sort a two dimensional array of values from the candidate rows and criterion columns. This two dimensional array will be built lazily, that is: iff the first column is detected by the comparitor framework to be insufficent to provide a ‘complete’ order (ie it detects equality at runtime between a pair of elements) then the on the needed portions of the second column will be obtained, and so on until an inequality is discovered between the two candidates or we run out of criteria. var gc =

          candidate_ids_in_new_order = pndr.get_sorted_candidate_ids(args)
          pndr.reorder_table_rows_according_to_candidate_order candidate_ids_in_new_order
    
        reorder_table_rows_according_to_candidate_order: (new_order) ->
  • ¶

    first move every one of the TRs into the tBody in the new order

          i = undefined
          cand = undefined
          i = 0
          while i < ptw_vars.candidates_by_row_idx.length
            cand = ptw_vars.candidates_by_row_idx[i]
            continue  unless cand
  • ¶

    console.log(i,”is now “,cand.label());

            tr = cand.tr()
  • ¶

    see ref#8

            if i + 1 >= ptw_vars.candidates_by_row_idx.length
              ptw_vars.candidate_rows.appendChild tr
            else
              ptw_vars.candidate_rows.insertBefore tr, ptw_vars.candidate_rows.children[i + 1]
            cand.idx i
            i++
  • ¶

    ensure that candidates_by_row_idx is properly indexed

          for i of ptw_vars.candidates_by_their_id
            cand = ptw_vars.candidates_by_their_id[i]
  • ¶

    console.log(“registering “+cand.label()+” at “+cand.idx());

            ptw_vars.candidates_by_row_idx[cand.idx()] = cand
    
        get_sorted_candidate_ids: (args) ->
  • ¶

    In args we look for both the list args.criteria and optionally, the object args.directions, which (if it is present) will be keyed by the crit.id() and contain the values ‘ASC’|’DESC’|undefined, undefined meaning that criterion should not participate in the ordering.

          crit_comparitors = []
          num_crit_shown = pndr.get_num_crit_unshaded()
  • ¶

    console.log(‘shown:’,num_crit_shown);

          idx = 0
    
          while idx < args.criteria.length
            crit_obj = args.criteria[idx]
            if idx < num_crit_shown
              crit_comparitors.push
                cmp: crit_obj.get_cmp(args)
                id: crit_obj.id()
            idx++
    
          grand_comparitor = (cand_a, cand_b) ->
  • ¶

    cand_a and cand_b are candidate objects with method get_datum_by_crit_id

            retval = 0
            indent = ""
  • ¶

    console.log(“crit_comparitors:”,crit_comparitors);

            cci = 0
    
            while cci < crit_comparitors.length
              crit_id = crit_comparitors[cci].id
              crit = ptw_vars.criteria_by_their_id[crit_id]
              cmp = crit_comparitors[cci].cmp
              datum_a = cand_a.get_datum_by_crit_id(crit_id)
              datum_b = cand_b.get_datum_by_crit_id(crit_id)
  • ¶

    console.log(‘datum_a =’,datum_a);

              val_a = (if is_defined(datum_a) then datum_a.datum_coerce_to_valuetype() else `undefined`)
              val_b = (if is_defined(datum_b) then datum_b.datum_coerce_to_valuetype() else `undefined`)
              using = undefined
              if is_defined(val_a) and is_defined(val_b)
                using = "cmp"
                retval = cmp(val_a, val_b)
              else if is_defined(val_a) and is_undefined(val_b)
                using = "d&u"
                retval = -1
              else if is_undefined(val_a) and is_defined(val_b)
                using = "u&d"
                retval = 1
              else if is_undefined(val_a) and is_undefined(val_b)
                using = "u&u"
                retval = 0
              retval = retval * crit.default_order()
              summary = indent + "comparing " + using + " " + val_a + " and " + val_b + " ===> " + retval
              console.log summary  if window.DEBUG_COMPARITOR
  • ¶

    console.log(crit.label(),cand_a.label(),retval,cand_b.label());

              return retval  if retval isnt 0
              indent += "  "
              cci++
            retval = ascending_cmp(cand_a.label(), cand_b.label())  if retval is 0
            retval
    
          args.candidates.sort grand_comparitor
    
        fill_widget_from_ponderspec: (args) ->
          i = undefined
          ponderspec = args.ponderspec
  • ¶

    if (window.location.href.search(‘EDIT’) > -1){ ptw_vars.show_edit_button = true;

          if ptw_vars.show_edit_button
            pndr.set_visibility_of_controls()
          if ptw_vars.add_candidate_label
            adcand = pndr.getElementsByClassName("addCandidateButton")[0]
            adcand.value = ptw_vars.add_candidate_label
  • ¶

    These should be settable using the initial constructor Hmm. Do we really need the constructor and ‘make_widget_here’? Possibly. Let’s clean up as much as possible right now.

          copy_props_from_to_ [
             "readable_by", "writeable_by", "created_by", "creator_account",
             "modification_time", "creation_time", "authenticated_username"],
                  ponderspec, ptw_vars
          pndr.populate_a_select select_name: "writeable_by"
          pndr.populate_a_select select_name: "readable_by"
          if not ptw_vars.creator_account?
            console.log "creator_account is undefined"
          pndr.set_created_by(ptw_vars.creator_account?.displayName ||
                              ptw_vars.creator_account?.account_key ||
                              ptw_vars.created_by || 'hmm...')
          if ponderspec.name
            pndr.pndr8_title ponderspec.name
          else
            pndr.pndr8_title ptw_vars.pndr8_title
          pndr.ponderation_id ponderspec["_id"]  if ponderspec["_id"]
          if ponderspec.candidates
            i = 0
            while i < ponderspec.candidates.length
              pndr.add_candidate ponderspec.candidates[i]
              i++
          if ponderspec.criteria
            i = 0
            while i < ponderspec.criteria.length
              pndr.add_criterion ponderspec.criteria[i]
              i++
          if ponderspec.data
            i = 0
            while i < ponderspec.data.length
              datum_spec = ponderspec.data[i]
              pndr.add_datum datum_spec  if has_value(datum_spec)
              i++
          pndr.sort_candidates_if_unsorted()
          pndr.make_candidates_draggable_if_needed()
          pndr
    
        make_candidates_draggable_if_needed: ->
          if ptw_vars.candidate__drop_handler
            $(".candidateCell").attr("style","background-color:yellow")
  • ¶

    alert “whoa, hubba” TODO constrain selector to this ponder, for reentrancy

    ##

            $(".candidateCell").draggable
              simple_helper: "clone"
              helper: (e, ui) ->
  • ¶

    http://layout.jquery-dev.com/tips.cfm#Widget_Draggable

                return $(this).clone().appendTo('body').css('zIndex', 5).show()
              handle: ".candidateDragHandle"
              stop: ptw_vars.candidate__drop_handler
              start: ptw_vars.candidate__drag_start_handler
  • ¶

    ##

          pndr
  • ¶

    alert(window.location.href+” “+window.location.href.search(‘show_edit’));

        add_arrow_cell_to: (args) ->
          arrow_cell = args.target.insertCell(0)
          arrow_cell.setAttribute "class", "arrow_box"
          arrow_cell.setAttribute "rowspan", 1
  • ¶

    hide this arrow if it is on the first row

          $(arrow_cell).hide()  if args.target.rowIndex is 0
  • ¶

    arrow_cell.innerHTML = ptw_vars.basic_arrow; // replaced by bg via css

        being_edited: (b) ->
          if typeof (b) is typeof (true)
            ptw_vars.being_edited = b
          ptw_vars.being_edited
    
        is_spreadsheet_mode: ->
          return ptw_vars.interaction_mode is 'spreadsheet'
    
        is_document_mode: ->
          return ptw_vars.interaction_mode is 'document'
    
        make_cell_value_onchange_handler: (cell) ->
          return (evt) ->
            ptw_vars.cell_value_onchange_handler(evt, cell)
    
        get_num_crit_unshaded: (args) ->
          pndr.get_num_criteria() - ptw_vars.num_crit_shaded
    
        get_num_criteria: (args) ->
          ptw_vars.wgt_table.tHead.children.length - 1
    
        get_num_columns: (args) ->
          ptw_vars.wgt_table.tHead.children.length
    
        add_column_to_candidates: (args) ->
  • ¶

    console.log(‘adding column ‘,args.crit_obj.label(), ‘ to candidates’);

          a_cand_row = undefined
  • ¶

    console.log(“candidate_rows:”,ptw_vars.candidate_rows); var candidate_rows = ptw_vars.candidate_rows; console.log(‘num candidates:’,ptw_vars.candidate_rows.children.length); console.log(ptw_vars.candidates_by_row_idx); for (var idx = 0; idx <= ptw_vars.candidate_rows.childElementCount; idx++){

          idx = 0
    
          while idx < ptw_vars.candidates_by_row_idx.length
            a_cand_row = ptw_vars.candidate_rows.children[idx]
            continue  unless a_cand_row # TODO(shawn) were you being paid by the mystery?
            a_cand_obj = ptw_vars.candidates_by_row_idx[idx]
            continue  unless a_cand_obj # TODO(shawn) were you being paid by the misery?
            a_cand_obj.add_column args
            idx++
    
        get_valuetype_by_id: (valuetype_id) ->
          valuetype_factory.get valuetype_id
    
        push_down_criteria: (args) ->
          a_crit_row = undefined
  • ¶

    TODO de-clever the following horror (maybe see what the javascript is doing!)

          pndr.add_arrow_cell_to target: ptw_vars.wgt_controls  if ptw_vars.wgt_controls.children.length < 2
          thead = ptw_vars.wgt_table.tHead
          idx = thead.children.length - 1
    
          while idx > -1
            a_crit_row = thead.children[idx]
            a_criterion = ptw_vars.criteria_by_row_idx[idx]
  • ¶

    TODO vewwy vewwy kawfuwwy unfuck this construct

            continue  if args.except.row_idx is idx  if args.except isnt `undefined`
  • ¶

    if (idx == 0) continue;

            if a_criterion
              a_criterion.push_down()
            idx--
    
        wait: (msecs) ->
          start = new Date().getTime()
          cur = start
          cur = new Date().getTime()  while cur - start < msecs
    
        ponderation_id: (s) ->
          ptw_vars._id = s  if typeof (s) is typeof ("")
          ptw_vars._id
    
        has_unsaved_changes: (b) ->
          ptw_vars.has_unsaved_changes = b  if typeof (b) is "boolean"
          ptw_vars.has_unsaved_changes
    
        write_to_storage: (obj) ->
  • ¶

    This will write to the server or local storage as needed. return true when that works. If true, there is no need for dirtiness, the save button, etc. If false, the save button is needed.

          false
    
        ondrag_windowshade: (args) ->
          console.log "ondrag", args
          console.log "arguments", arguments_
    
        candidates_unsorted: (b) ->
          ptw_vars.candidates_unsorted = b  if is_boolean(b)
          ptw_vars.candidates_unsorted or false
    
        xsd_to_valuetype: (xsd_type) ->
          return xsd_type_to_valuetype(xsd_type)
    
      pndr = that
      return pndr
    
    if document?
      document.ontouchmove = (e) ->
        e.preventDefault()
    
    (exports ? this).localize_iso8601 = localize_iso8601
  • ¶

    (exports ? this).validate_iso8601 = validate_iso8601

    (exports ? this).tabular_widget = tabular_widget
  • ¶

    console.log(“loading tabular_widget.js”); Ponderate.op_queue__drain();

    if Ponderate.set_of_params
      console.log "Ponderate.set_of_params() =", Ponderate.set_of_params
  • ¶

    window.ptw = Ponderate.tabular_widget(); Ponderate.register(ptw.make_width_here(Ponderate.set_of_params[0]));

      Ponderate.embed_desired_widget()